package parser.astgen;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import ocllibrary.OclLibrary;
import parser.astlib.OclActualParameterListItem;
import parser.astlib.OclAttrOrAssocContextDecl;
import parser.astlib.OclAttributeDefinedEntityDecl;
import parser.astlib.OclBinaryExpTail;
import parser.astlib.OclClassifierContextDecl;
import parser.astlib.OclContextDeclaration;
import parser.astlib.OclDefinedEntityDecl;
import parser.astlib.OclDefinitionClassifierConstraint;
import parser.astlib.OclDefinitionConstraint;
import parser.astlib.OclDeriveConstraint;
import parser.astlib.OclFormalParameter;
import parser.astlib.OclInitConstraint;
import parser.astlib.OclInvariantClassifierConstraint;
import parser.astlib.OclOperationConstraint;
import parser.astlib.OclOperationConstraintStereotype;
import parser.astlib.OclOperationContextDecl;
import parser.astlib.OclOperationDefinedEntityDecl;
import parser.astlib.OclOperationSignature;
import parser.astlib.OclTimeExp;
import parser.sablecc.analysis.AttrEvalException;
import parser.sablecc.analysis.LAttrEvalAdapter;
import parser.sablecc.node.AArgListPropertyCallExpCs;
import parser.sablecc.node.AAttrOrAssocContextDeclarationCs;
import parser.sablecc.node.AClassifierContextDeclarationCs;
import parser.sablecc.node.ADefinitionConstraintCs;
import parser.sablecc.node.AIterateArrowPropertyCallExpCs;
import parser.sablecc.node.AIterateVarsCs;
import parser.sablecc.node.AIteratorArrowPropertyCallExpCs;
import parser.sablecc.node.AIteratorVarsCs;
import parser.sablecc.node.ALetExpCs;
import parser.sablecc.node.AOperationArrowPropertyCallExpCs;
import parser.sablecc.node.AOperationConstraintCs;
import parser.sablecc.node.AOperationContextDeclarationCs;
import parser.sablecc.node.APropPostfixExpTailCs;
import parser.sablecc.node.APropertyCallParametersCs;
import parser.sablecc.node.APropertyPrimaryExpCs;
import parser.sablecc.node.AQualifiedPropertyCallExpCs;
import parser.sablecc.node.AQualifiers;
import parser.sablecc.node.AWithTailPostfixExpCs;
import parser.sablecc.node.PActualParameterListCs;
import parser.sablecc.node.PClassifierConstraintCs;
import parser.sablecc.node.PExpression;
import parser.sablecc.node.PInitOrDerValueCs;
import parser.sablecc.node.PIterateVarsCs;
import parser.sablecc.node.PIteratorVarsCs;
import parser.sablecc.node.POclExpressionCs;
import parser.sablecc.node.POperationConstraintCs;
import parser.sablecc.node.PPostfixExpTailCs;
import parser.sablecc.node.PPropertyCallExpCs;
import parser.sablecc.node.PPropertyCallParametersCs;
import parser.sablecc.node.PQualifiers;
import parser.sablecc.node.PQualifiersListCs;
import parser.sablecc.node.TFalse;
import parser.sablecc.node.TIntegerLiteral;
import parser.sablecc.node.TRealLiteral;
import parser.sablecc.node.TTrue;
import parser.util.Sequence;
import parser.util.Transformations;
import parser.util.TypeCheck;
import parser.util.Utility;
import project.Project;
import uml2.associationclasses.AssociationClass;
import uml2.kernel.Classifier;
import uml2.kernel.Constraint;
import uml2.kernel.DataType;
import uml2.kernel.Element;
import uml2.kernel.Enumeration;
import uml2.kernel.EnumerationLiteral;
import uml2.kernel.Operation;
import uml2.kernel.Parameter;
import uml2.kernel.ParameterDirectionKindEnum;
import uml2.kernel.Property;
import uml2.kernel.Type;
import uml2.kernel.UmlClass;
import uml2.ocl2.AArgumentParentCall;
import uml2.ocl2.AnyType;
import uml2.ocl2.AssociationClassCallExp;
import uml2.ocl2.BooleanLiteralExp;
import uml2.ocl2.CollectionItem;
import uml2.ocl2.CollectionKind;
import uml2.ocl2.CollectionKindEnum;
import uml2.ocl2.CollectionLiteralExp;
import uml2.ocl2.CollectionLiteralPart;
import uml2.ocl2.CollectionRange;
import uml2.ocl2.CollectionType;
import uml2.ocl2.EnumLiteralExp;
import uml2.ocl2.ExpressionInOcl;
import uml2.ocl2.IfExp;
import uml2.ocl2.IntegerLiteralExp;
import uml2.ocl2.IterateExp;
import uml2.ocl2.IteratorExp;
import uml2.ocl2.LetExp;
import uml2.ocl2.LoopExp;
import uml2.ocl2.OclExpression;
import uml2.ocl2.OperationCallExp;
import uml2.ocl2.PropertyCallExp;
import uml2.ocl2.RealLiteralExp;
import uml2.ocl2.SetType;
import uml2.ocl2.StringLiteralExp;
import uml2.ocl2.TupleLiteralExp;
import uml2.ocl2.TupleLiteralPart;
import uml2.ocl2.TupleType;
import uml2.ocl2.TypeExp;
import uml2.ocl2.Variable;
import uml2.ocl2.VariableExp;
import exceptions.ConstraintException;
import facade.UserFacade;
import facade.kernel.AConstrainedElementConstraintOfConstrainedElementFacade;
import facade.kernel.AIteratorLoopExpFacade;
import facade.kernel.ConstraintFacade;
import facade.ocl.AArgumentParentCallFacade;
import facade.ocl.ExpressionInOclFacade;
import facade.ocl.VariableFacade;

/**
 * @author Antonio Villegas
 *
 */
public class LAttrGMCAstGenerator extends LAttrEvalAdapter {
	
	// NodeFactory factory -> inherited from LAttrEvalAdapter
	private Project p;
    private UserFacade uf;
    private Utility ut;
    private TypeCheck tc;
    private AArgumentParentCallFacade apcf;
    private Transformations tr;
    public boolean envIsSet = false;
    private OclLibrary ocl;
    
    private Sequence anonIterVars = new Sequence("anonIterVar");
    
    private static final String ANONYMOUS_ITERATOR_NAME = "";
    
    /**
     * Constructor method
     * @param p the actual project in use
     */
	public LAttrGMCAstGenerator(Project p) {
		this.p = p;
		this.uf = new UserFacade(p);
		this.setNodeFactory(new NodeFactory(p));
		this.ut = new Utility(this.uf, this.p);
		this.tc = new TypeCheck(this.p, factory);
		this.apcf = new AArgumentParentCallFacade(this.p);
		this.tr = new Transformations(factory);
		this.ocl = new OclLibrary(p);
		this.ocl.setTypes();
	}
	
	//************************************************************************
	//************************************************************************
	//************************************************************************
	
	
	
	//************************************************************************
	//*********************** Constraints Structure **************************
	//************************************************************************
	public List computeAstFor_AContextDeclarationListCs(Heritage nodeHrtg,
			OclContextDeclaration astContext, List astTail)
			throws AttrEvalException {
		// Creates a new list with astContext and astTail, and returns it.
		// This new list's elements are OclContextDeclaration instances
		List result = null;
        if ( astTail != null ) {
            astTail.add(0, astContext);
            result = astTail;
        } else {
            result = new LinkedList();
            result.add(astContext);
        }
        return result;
	}
	
	
			//+++++++++++++++++++++++++++++++
			// CLASSIFIER CONTEXT DECLARATION (inv)
			//+++++++++++++++++++++++++++++++
	public OclClassifierContextDecl computeAstFor_AClassifierContextDeclarationCs(
			OclClassifierContextDecl myAst, Heritage nodeHrtg,
			List astContextName, List astListConstraints)
			throws AttrEvalException {
		
		// Because all work has been done in the descendents, only must complete the attributes here
		myAst.setContextName(astContextName);
		// astListConstraints is != null, grammar ensures this
		myAst.setConstraints(astListConstraints);
		return myAst;
	}
	public OclInvariantClassifierConstraint computeAstFor_AInvariantClassifierConstraintCs(
			OclInvariantClassifierConstraint myAst, Heritage nodeHrtg,
			String astName, OclExpression astInvariant)
			throws AttrEvalException {

		// Type of invariants must be boolean
		Type inv = tc.typecheck(astInvariant);
		if (inv!=null) {
			if (!inv.equals(uf.findDataTypeByName("Boolean")) || astInvariant instanceof PropertyCallExp) {
				// Case study added only for improve the feedback
				if (astInvariant instanceof PropertyCallExp) {
					PropertyCallExp pce = (PropertyCallExp) astInvariant;
					throw new AttrEvalException("Invariant must be boolean, but it is "+ut.getNameOfResultTypeOfNavigation(pce)+
							"\nPlease complete the expression in order to achieve a boolean type.");
				}
				throw new AttrEvalException("Invariant must be boolean, but it is "+inv.getName()+
						"\nPlease complete the expression in order to achieve a boolean type.");
			} 
		} 
		else throw new AttrEvalException("Invariant must be boolean, but the expression's type is null. " +
				"\nContact to the developer in order to solve this problem");
		
		// Obtain Context Classifier from Heritage
        Classifier ctxCls = nodeHrtg.getContextualClassifier();
        
        // Create ExpressionInOcl   
        ExpressionInOclFacade eiof = new ExpressionInOclFacade(p);
    	ExpressionInOcl eio = eiof.createExpressionInOcl();
    	eio.setSymbol("inv");
    	
    	// Create Context Variable and set into ExpressionInOCL
    	VariableFacade vf = new VariableFacade(p);
    	Variable v = vf.createVariable();
    	v.setType(ctxCls);
    	eio.setContextVariable(v);
    	
    	// Set astInvariant (OCLExpression) into ExpressionInOCL as Body
    	eio.setBodyExpression(astInvariant);
        
        // Create Constraint...
    	ConstraintFacade constr = new ConstraintFacade(p);
    	Constraint c = constr.createConstraint(astName, uml2.kernel.VisibilityKindEnum.PUBLIC);
    	// ...and set ExpressionInOCL into it as Specification
    	c.setSpecification(eio);
    	
    	if (verbose) System.out.println("ExpressionInOcl created successfully");
    	
    	// Link Constraint and Context Classifier
    	AConstrainedElementConstraintOfConstrainedElementFacade aceccef = new AConstrainedElementConstraintOfConstrainedElementFacade(p);
    	try {
			aceccef.getAConstrainedElementConstraintOfConstrainedElement().add(ctxCls, c);
		} catch (ConstraintException e) {
			if (verbose) System.out.println("Error linking constraint and context");
			e.printStackTrace();
		}
        
		if (verbose) System.out.println("Constraint created successfully");
		
        myAst.setName(astName);
        myAst.setInvariant(astInvariant);        
        return myAst;
	}

	// Heritage for Classifier Constraints Structure 
	public Heritage insideAClassifierContextDeclarationCs_computeHeritageFor_Constraints(
			AClassifierContextDeclarationCs parent,
			PClassifierConstraintCs child, Heritage parentHrtgCopy,
			List astContextName) throws AttrEvalException {
		
		Heritage resultHeritage = parentHrtgCopy;
		//Environment parentEnv = parentHrtgCopy.getEnv();
		
		// Busquem la clase del contexte
		int numNameElements = astContextName.size();
		if ( numNameElements == 1 ) {
            String name = (String) astContextName.get(0);
            UmlClass contextclass = uf.findClassByName(name);
            if (contextclass == null) {
            	throw new AttrEvalException("L'element "+name+" no apareix al model");
            }
            
            // S'ha de decidir si escollim mantenir l'environment anterior o usar un de nou
            //WritableEnvironment expressionEnv = (WritableEnvironment) parentEnv;
            WritableEnvironment expressionEnv = EnvironmentFactory.EMPTY_ENV();
            
           	// create apropriate environment
            // Com que no s'utilitza, ho deixem comentat
           	/*Namespace contextualClassifierNs = NamespaceUtility.createNamespaceFor(contextclass);
           	try {
               	expressionEnv.addNamespace(contextualClassifierNs);
           	} catch ( DuplicateNameException dne ) {
               	rethrowDNE(dne, "namespace of contextual classifier '" + pathNameToString(astContextName) + "'");
           	}*/       
           
            // Busquem al repositori si existeix una variable self del tipus del contextclass
            Variable self = ut.findVariableByNameAndClass("self", contextclass);
           	try {
           		if (self==null) {
           			// si no existeix, la creem
           			self = createVariableForSelf(contextclass);
           		}
           		// finalment la fiquem a l'environment actual
           		expressionEnv.addElement("self", self, true);
            } catch ( DuplicateNameException dne ) {
            	rethrowDNE(dne, "contextual classifier 'self'");
            }
            
            // Muntem l'Heritage resultant
            resultHeritage.setContextualClassifier(contextclass);
            resultHeritage.setConstrainedElement(contextclass);
            resultHeritage.setEnv(expressionEnv);
            
		}
		else {
			throw new AttrEvalException("Utilitzar paths al context encara no esta suportat");
		}
		return resultHeritage;
	}
	
	
			//+++++++++++++++++++++++++++++++++++++++++++++++++
			// ATTRIBUTE OR ASSOCIATION END CONTEXT DECLARATION (init, derive)
			//+++++++++++++++++++++++++++++++++++++++++++++++++
	public OclAttrOrAssocContextDecl computeAstFor_AAttrOrAssocContextDeclarationCs(
			OclAttrOrAssocContextDecl myAst, Heritage nodeHrtg,
			List astContextName, Classifier astType, List astListConstraints)
			throws AttrEvalException {
		myAst.setContextName(astContextName);
        myAst.setTypeSpecifier(astType);
        if ( astListConstraints == null ) {
            myAst.setConstraintList(astListConstraints);
        } else {
            myAst.getConstraintList().clear();
        }
        return myAst;
	}
	public OclDeriveConstraint computeAstFor_ADeriveInitOrDerValueCs(
			OclDeriveConstraint myAst, Heritage nodeHrtg, String astName,
			OclExpression astDeriveExpression) throws AttrEvalException {
		// Obtain Context Classifier and constrained element from Heritage
        Classifier ctxCls = nodeHrtg.getContextualClassifier();
        Property constrainedElement = (Property) nodeHrtg.getConstrainedElement();
        Type tprop = constrainedElement.getType();
        
        // Type of derivation must conform with constrained element type
		Type init = tc.typecheck(astDeriveExpression);
		init = ut.getCorrectType(astDeriveExpression);
		if (init!=null) {
			if (!init.equals(tprop)) {
				throw new AttrEvalException("Derivation must be "+tprop.getName()+", but it is "+init.getName()+
						"\nPlease complete the expression in order to achieve a "+tprop.getName()+" type.");
			} 
		} 
		else throw new AttrEvalException("Derivation expression must be "+tprop.getName()+", but " +
				"it is null.\nContact to the developer in order to solve this problem");
        
        // Create ExpressionInOcl   
        ExpressionInOclFacade eiof = new ExpressionInOclFacade(p);
    	ExpressionInOcl eio = eiof.createExpressionInOcl();
    	eio.setSymbol("derive");
    	
    	// Create Context Variable and set into ExpressionInOCL
    	VariableFacade vf = new VariableFacade(p);
    	Variable v = vf.createVariable();
    	v.setType(ctxCls);
    	eio.setContextVariable(v);
    	
    	// Set astInvariant (OCLExpression) into ExpressionInOCL as Body
    	eio.setBodyExpression(astDeriveExpression);
    	
    	// Create Constraint...
    	ConstraintFacade constr = new ConstraintFacade(p);
    	Constraint c = constr.createConstraint(astName, uml2.kernel.VisibilityKindEnum.PUBLIC);
    	// ...and set ExpressionInOCL into it as Specification
    	c.setSpecification(eio);
    	
    	if (verbose) System.out.println("ExpressionInOcl created successfully"); 	
    	
    	// Link Constraint and constrained element (Property)
    	AConstrainedElementConstraintOfConstrainedElementFacade aceccef = new AConstrainedElementConstraintOfConstrainedElementFacade(p);
    	try {
			aceccef.getAConstrainedElementConstraintOfConstrainedElement().add(constrainedElement, c);
		} catch (ConstraintException e) {
			if (verbose) System.out.println("Error linking constraint and context");
			e.printStackTrace();
		}
        
		if (verbose) System.out.println("Constraint created successfully");
		
        myAst.setExpression(astDeriveExpression);      
        return myAst;

	}
	public OclInitConstraint computeAstFor_AInitInitOrDerValueCs(
			OclInitConstraint myAst, Heritage nodeHrtg, String astName,
			OclExpression astInitializer) throws AttrEvalException {
		
		// Obtain Context Classifier and constrained element from Heritage
        Classifier ctxCls = nodeHrtg.getContextualClassifier();
        Property constrainedElement = (Property) nodeHrtg.getConstrainedElement();
        Type tprop = constrainedElement.getType();
        
        // Type of initialization must conform with constrained element type
		Type init = tc.typecheck(astInitializer);
		init = ut.getCorrectType(astInitializer);
		if (init!=null) {
			if (!tc.existsCompatibility(tprop, init)) {
				if (tprop instanceof CollectionType && init instanceof CollectionType) {
            		if (ut.haveSameCollectionKind(tprop, init)) {
            			if (((CollectionType)init).getElementType()!=null) {
            				throw new AttrEvalException("Initializer must be "+tprop.getName()+", but it is "+init.getName()+
            						"\nPlease complete the expression in order to achieve a "+tprop.getName()+" type.");
            			}
            		}
            		else throw new AttrEvalException("Initializer must be "+tprop.getName()+", but it is "+init.getName()+
    						"\nPlease complete the expression in order to achieve a "+tprop.getName()+" type.");
            	}
            	else throw new AttrEvalException("Initializer must be "+tprop.getName()+", but it is "+init.getName()+
						"\nPlease complete the expression in order to achieve a "+tprop.getName()+" type.");
			} 
		} 
		else throw new AttrEvalException("Initializer expression must be "+tprop.getName()+", but " +
				"it is null.\nContact to the developer in order to solve this problem");
        
        // Create ExpressionInOcl   
        ExpressionInOclFacade eiof = new ExpressionInOclFacade(p);
    	ExpressionInOcl eio = eiof.createExpressionInOcl();
    	eio.setSymbol("init");
    	
    	// Create Context Variable and set into ExpressionInOCL
    	VariableFacade vf = new VariableFacade(p);
    	Variable v = vf.createVariable();
    	v.setType(ctxCls);
    	eio.setContextVariable(v);
    	
    	// Set astInvariant (OCLExpression) into ExpressionInOCL as Body
    	eio.setBodyExpression(astInitializer);
    	
    	// Create Constraint...
    	ConstraintFacade constr = new ConstraintFacade(p);
    	Constraint c = constr.createConstraint(astName, uml2.kernel.VisibilityKindEnum.PUBLIC);
    	// ...and set ExpressionInOCL into it as Specification
    	c.setSpecification(eio);
    	
    	if (verbose) System.out.println("ExpressionInOcl created successfully"); 	
    	
    	// Link Constraint and constrained element (Property)
    	AConstrainedElementConstraintOfConstrainedElementFacade aceccef = new AConstrainedElementConstraintOfConstrainedElementFacade(p);
    	try {
			aceccef.getAConstrainedElementConstraintOfConstrainedElement().add(constrainedElement, c);
		} catch (ConstraintException e) {
			if (verbose) System.out.println("Error linking constraint and context");
			e.printStackTrace();
		}
        
		if (verbose) System.out.println("Constraint created successfully");
		
        myAst.setExpression(astInitializer);        
        return myAst;
	}
	
	// Heritage for Attribute or AssociationEnd Constraints Structure
	public Heritage insideAAttrOrAssocContextDeclarationCs_computeHeritageFor_Constraints(
			AAttrOrAssocContextDeclarationCs parent, PInitOrDerValueCs child,
			Heritage parentHrtgCopy, List astContextName, Classifier astType)
			throws AttrEvalException {
		 
		Heritage resultHrtg = parentHrtgCopy;
		Environment parentEnv = parentHrtgCopy.getEnv();
	        
		// check minimum length requirement of path name
		int contextNameSize = astContextName.size();
		if (  contextNameSize != 2 ) {
			throw new AttrEvalException("Need at two components in pathname here");
		}
		// split path name into name for contextual classifier and name of 
		// constrained item inside contextual classifier 
		String attrName = (String) astContextName.get(1);
		String owningElementName = (String) astContextName.get(0);

		// lookup the context element and the attribute
		UmlClass context = uf.findClassByName(owningElementName);
		if (context == null) {
			throw new AttrEvalException("Unknown context " + owningElementName +
			" in current model");
		}
		Property attribute = ut.findPropertyByNameAndClass(attrName, context);
		if (attribute == null) {
			throw new AttrEvalException("Property "+attrName+" does not belong to context class "+owningElementName);
		}
		// check whether specified attribute type matches actual attribute type
		if (!attribute.getType().getName().equals(astType.getName())) {
			throw new AttrEvalException("Incompatible types: Property "+attrName+" has "+attribute.getType().getName()+" ." +
					"\n Use it instead of "+astType.getName()+" in the context declaration.");
		}
		
		Classifier contextualClassifier = (Classifier) context;

		WritableEnvironment expressionEnv = EnvironmentFactory.EMPTY_ENV();
		
		Variable self = ut.findVariableByNameAndClass("self", context);
       	try {
       		if (self==null) {
       			// si no existeix, la creem
       			self = createVariableForSelf(context);
       		}
       		// finalment la fiquem a l'environment actual
       		expressionEnv.addElement("self", self, true);
        } catch ( DuplicateNameException dne ) {
        	rethrowDNE(dne, "contextual classifier 'self'");
        }
		
        // create/modify result heritage
        resultHrtg.setConstrainedElement(attribute);
        resultHrtg.setContextualClassifier(contextualClassifier);
        resultHrtg.setEnv(expressionEnv);
    
    return resultHrtg;
	}

	
			//+++++++++++++++++++++++++++++++++++++++++++++++++
			// ATTRIBUTE OR OPERATION CONTEXT DECLARATION (def)
			//+++++++++++++++++++++++++++++++++++++++++++++++++
	public OclDefinitionClassifierConstraint computeAstFor_ADefinitionClassifierConstraintCs(
			OclDefinitionClassifierConstraint myAst, Heritage nodeHrtg,
			String astName, OclDefinitionConstraint astDefinition)
			throws AttrEvalException {
		
		Classifier ctxCls = nodeHrtg.getContextualClassifier();
		// Create ExpressionInOcl   
        ExpressionInOclFacade eiof = new ExpressionInOclFacade(p);
    	ExpressionInOcl expOcl = eiof.createExpressionInOcl();
    	// we use symbol as the stereotype
    	expOcl.setSymbol("def");
        expOcl.setBodyExpression(astDefinition.getDefinition());
        
        // Create Context Variable and set into ExpressionInOCL
    	VariableFacade vf = new VariableFacade(p);
    	Variable v = vf.createVariable();
    	v.setType(ctxCls);
    	expOcl.setContextVariable(v);

    	// Create Constraint...
    	ConstraintFacade constr = new ConstraintFacade(p);
    	Constraint cons = constr.createConstraint(astName, uml2.kernel.VisibilityKindEnum.PUBLIC);
    	// ...and set ExpressionInOCL into it as Specification
    	cons.setSpecification(expOcl);
    	
    	if (verbose) System.out.println("ExpressionInOcl created successfully"); 	

        OclDefinedEntityDecl deDecl = astDefinition.getEntity();
        OclDefinedEntityDecl.EntityType type = deDecl.getType();        

        // ATTRIBUTE DEFINITION CASE
        if ( OclDefinedEntityDecl.EntityType.ATTRIBUTE.equals(type) ) {
            OclAttributeDefinedEntityDecl adecl = deDecl.asAttributeDeclaration();
            
            OclFormalParameter fp = adecl.getAttributeDeclaration();
            String propname = fp.getName();
            Type proptype = fp.getType();
            
            // check different restrictions
            // 1- the attribute does not exists in the model for the contextual class
            List attributes = ((UmlClass)ctxCls).getOwnedAttribute();
            for (Iterator iterator = attributes.iterator(); iterator.hasNext();) {
				Property classprop = (Property) iterator.next();
				if (classprop.getName().equals(propname)) {
					throw new AttrEvalException("There is another previous property "+propname+" at "+ctxCls.getName()+" class." +
							"\nUse another name instead of "+propname);
				}
			}
            
            // 2- the type of the attribute is compatible with its definition
            OclExpression definition = astDefinition.getDefinition();
            tc.typecheck(definition);
            Type deftype = ut.getCorrectType(definition);
            if (!tc.existsCompatibility(proptype, deftype)) {
            	if (proptype instanceof CollectionType && deftype instanceof CollectionType) {
            		if (ut.haveSameCollectionKind(proptype, deftype)) {
            			if (((CollectionType)deftype).getElementType()!=null) {
            				throw new AttrEvalException("Incompatibility of types between property type "+proptype.getName()+
                        			" and definition type "+deftype.getName());
            			}
            		}
            		else throw new AttrEvalException("Incompatibility of types between property type "+proptype.getName()+
                			" and definition type "+deftype.getName());
            	}
            	else throw new AttrEvalException("Incompatibility of types between property type "+proptype.getName()+
            			" and definition type "+deftype.getName());
            }
            // create the new attribute
            Property prop = (Property) this.factory.createNode("Property");
            prop.setName(propname);
            prop.setType(proptype);
            try {
				prop.setUmlclass((UmlClass) ctxCls);
			} catch (ConstraintException e1) {
				e1.printStackTrace();
				throw new AttrEvalException(e1.getMessage());
			}
            
            // Link Constraint and constrained element (Property)
        	AConstrainedElementConstraintOfConstrainedElementFacade aceccef = new AConstrainedElementConstraintOfConstrainedElementFacade(p);
        	try {
    			aceccef.getAConstrainedElementConstraintOfConstrainedElement().add(prop, cons);
    		} catch (ConstraintException e) {
    			e.printStackTrace();
    			throw new AttrEvalException(e.getMessage());
    		}
            
    		if (verbose) System.out.println("Constraint created successfully");
            

    	// OPERATION DEFINITION CASE    
        } else if ( OclDefinedEntityDecl.EntityType.OPERATION.equals(type) ) {
            OclOperationDefinedEntityDecl odecl = deDecl.asOperationDeclaration();
            OclOperationSignature osig = odecl.getOperationSignature();
            
            // Params, result type and operation name
            List opParams = osig.getFormalParameters();
            Type opResType = osig.getReturnType();
            String opName = odecl.getOperationName();
            
            boolean withoutReturn = false;
            if (opResType == null) withoutReturn = true;
            
            // check different restrictions
            // 1- the operation does not exists in the model for the contextual class
            List operations = ((UmlClass)ctxCls).getOwnedOperation();
            for (Iterator iterator = operations.iterator(); iterator.hasNext();) {
				Operation classop = (Operation) iterator.next();
				if (classop.getName().equals(opName)) {
					List params = classop.getOwnedParameter();
					// preprocess the params list in order to extract the return parameter
					// because getType is not accessible and the return type is made with a return parameter
					LinkedList paramsNoReturn = new LinkedList();
					boolean samereturn = true;
					boolean hasreturn = false;
					for (Iterator iterator2 = params.iterator(); iterator2.hasNext();) {
						Parameter par = (Parameter) iterator2.next();
						if (!par.getDirection().equals(ParameterDirectionKindEnum.RETURN))
							paramsNoReturn.add(par);
						else {
							hasreturn = true;
							if (withoutReturn) {
								samereturn = false;
							}
							else if (!par.getType().getName().equals(opResType.getName())) {
								samereturn = false;
							}
						}
					}
					
					if (withoutReturn && hasreturn) samereturn = false;
					if (!withoutReturn && !hasreturn) samereturn = false;
					
					//check if the parameters are equals using paramsNoReturn list
					if (opParams.size()==paramsNoReturn.size()) {
						boolean sameparams = true;
						for (int i=0; i<paramsNoReturn.size() && sameparams && samereturn ; ++i) {
							OclFormalParameter fp = (OclFormalParameter) opParams.get(i);
							Parameter realpar = (Parameter) paramsNoReturn.get(i);
							// same type and same name of parameters
							if (fp.getName().equals(realpar.getName()) &&
								fp.getType().getName().equals(realpar.getType().getName())) {
								sameparams = true;
							}else sameparams = false;
						}// endfor
						System.out.println("Sameparams: "+sameparams+", samereturn: "+samereturn);
						if (sameparams && samereturn) {
							throw new AttrEvalException("There is another previous operation "+opName+" at "+ctxCls.getName()+
									" class with the same name and parameters.\nUse another operation signature");
						}
					}// endif
				}
			}// endfor
            
            // 2- the type of the operation return parameter is compatible with its definition type
            OclExpression definition = astDefinition.getDefinition();
            tc.typecheck(definition);
            Type deftype = ut.getCorrectType(definition);
            if (opResType!=null) {
            	if (!tc.existsCompatibility(opResType, deftype))
            		throw new AttrEvalException("Incompatibility of types between operation "+opName+" result type "+opResType.getName()+
            			" and definition type "+deftype.getName());
            }
            // create the new operation
            Operation newOp = (Operation) factory.createNode("Operation");
            newOp.setName(opName);
            try {
				newOp.setUmlclass((UmlClass) ctxCls);
			} catch (ConstraintException e1) {
				e1.printStackTrace();
				throw new AttrEvalException(e1.getMessage());
			}
			// create the parameters
			for (Iterator iterator = opParams.iterator(); iterator.hasNext();) {
				OclFormalParameter ofp = (OclFormalParameter) iterator.next();
				Parameter newpar = (Parameter) factory.createNode("Parameter");
				newpar.setName(ofp.getName());
				newpar.setType(ofp.getType());
				newpar.setDirection(ParameterDirectionKindEnum.IN);
				try {
					newpar.setOperation(newOp);
				} catch (ConstraintException e) {
					e.printStackTrace();
					throw new AttrEvalException(e.getMessage());
				}
			}
			//create the return parameter if necessary
			if (opResType!=null) {
				Parameter newpar = (Parameter) factory.createNode("Parameter");
				System.out.println("Return parameter created");
				newpar.setType(opResType);
				newpar.setDirection(ParameterDirectionKindEnum.RETURN);
				try {
					newpar.setOperation(newOp);
				} catch (ConstraintException e) {
					e.printStackTrace();
					throw new AttrEvalException(e.getMessage());
				}
			}
			
			// Link Constraint and constrained element (Operation)
        	AConstrainedElementConstraintOfConstrainedElementFacade aceccef = new AConstrainedElementConstraintOfConstrainedElementFacade(p);
        	try {
    			aceccef.getAConstrainedElementConstraintOfConstrainedElement().add(newOp, cons);
    		} catch (ConstraintException e) {
    			e.printStackTrace();
    			throw new AttrEvalException(e.getMessage());
    		}
            
    		if (verbose) System.out.println("Constraint created successfully");
			
        } else {
            throw new RuntimeException("Unknown entity type " + type + " in OCL 'def' constraint");
        }
        
        myAst.setName(astName);
        myAst.setDefinition(astDefinition);
        return myAst;
	}
	public OclDefinitionConstraint computeAstFor_ADefinitionConstraintCs(
			OclDefinitionConstraint myAst, Heritage nodeHrtg,
			OclDefinedEntityDecl astEntity, OclExpression astDefinition)
			throws AttrEvalException {
		myAst.setEntity(astEntity);
        myAst.setDefinition(astDefinition);
        return myAst;
	}	
	public OclAttributeDefinedEntityDecl computeAstFor_AAttributeDefinedEntityDeclCs(
			OclAttributeDefinedEntityDecl myAst, Heritage nodeHrtg,
			OclFormalParameter astAttribute) throws AttrEvalException {
		myAst.setAttributeDeclaration(astAttribute);
        return myAst;
	}
	public OclOperationDefinedEntityDecl computeAstFor_AOperationDefinedEntityDeclCs(
			OclOperationDefinedEntityDecl myAst, Heritage nodeHrtg,
			String astOperationName, OclOperationSignature astOperation)
			throws AttrEvalException {
		myAst.setOperationName(astOperationName);
        myAst.setOperationSignature(astOperation);
        return myAst;
	}	
	
	// Heritage for Attribute or Operation Constraints Structure (Definition)
	public Heritage insideADefinitionConstraintCs_computeHeritageFor_Definition(
			ADefinitionConstraintCs parent, POclExpressionCs child,
			Heritage parentHrtgCopy, OclDefinedEntityDecl astEntity)
			throws AttrEvalException {
		Heritage result = null;
		if ( astEntity.getType().equals(OclDefinedEntityDecl.EntityType.OPERATION ) ) {
			result = parentHrtgCopy;
			WritableEnvironment wrenv = result.getEnv().nestedEnvironment();
			
			OclOperationDefinedEntityDecl opdecl = astEntity.asOperationDeclaration();
			OclOperationSignature opsig = opdecl.getOperationSignature();
			
			// We obtain the parameters of the operation an iterate over them
			List params = opsig.getFormalParameters();
			Iterator it = params.iterator();
			while ( it.hasNext() ) {
				OclFormalParameter fp = (OclFormalParameter) it.next();
				String pname = fp.getName();
				if (pname == null)
					throw new AttrEvalException("Name of formal parameter must not be null");
				if ( pname.equals(""))
					throw new AttrEvalException("Name of formal parameter must not be an empty string");
				Type ptype = fp.getType();
				if (ptype == null)
					throw new AttrEvalException("Type of formal parameter "+pname+" must not be null");
				Variable v = (Variable) factory.createNode("Variable");
				v.setName(pname);
				v.setType(ptype);
				try {
					wrenv.addElement(pname, v, false);
				} catch (DuplicateNameException dne) {
					rethrowDNE(dne, "adding formal parameter variables to environment");
				}
			}
			result.setEnv(wrenv);
		}
		return result;
	}

	
	
			//+++++++++++++++++++++++++++++++++++++++++++++++++
			// OPERATION CONTEXT DECLARATION (pre, post, body)
			//+++++++++++++++++++++++++++++++++++++++++++++++++
	public OclOperationConstraint computeAstFor_AOperationConstraintCs(
			OclOperationConstraint myAst, Heritage nodeHrtgCopy,
			OclOperationConstraintStereotype astStereotype, String astName,
			OclExpression astExpression) throws AttrEvalException {
		
		Type constrType = tc.typecheck(astExpression);
		constrType = ut.getCorrectType(astExpression);
		
		if ((astStereotype.equals(astStereotype.PRE) ||
		     astStereotype.equals(astStereotype.POST))
		    && ( ! constrType.getName().equals("Boolean") )) {
		    throw new AttrEvalException("Type of pre- or post-condition must be 'Boolean', found " + constrType.getName() );
		}

		Heritage hrtg = nodeHrtgCopy;
		// Obtain Context Classifier from Heritage
        Classifier ctxCls = nodeHrtgCopy.getContextualClassifier();
        
        // Create ExpressionInOcl   
        ExpressionInOclFacade eiof = new ExpressionInOclFacade(p);
    	ExpressionInOcl eio = eiof.createExpressionInOcl();
    	eio.setSymbol(astStereotype.getName());
    	
    	// Create Context Variable and set into ExpressionInOCL
    	VariableFacade vf = new VariableFacade(p);
    	Variable v = vf.createVariable();
    	v.setType(ctxCls);
    	eio.setContextVariable(v);
    	
    	// Set astInvariant (OCLExpression) into ExpressionInOCL as Body
    	eio.setBodyExpression(astExpression);
        
        // Create Constraint...
    	ConstraintFacade constr = new ConstraintFacade(p);
    	Constraint c = constr.createConstraint(astName, uml2.kernel.VisibilityKindEnum.PUBLIC);
    	// ...and set ExpressionInOCL into it as Specification
    	c.setSpecification(eio);
    	
    	if (verbose) System.out.println("ExpressionInOcl created successfully");
    	
    	Element constrainedElement = hrtg.getConstrainedElement();
    	
    	// Link Constraint and Context Classifier
    	AConstrainedElementConstraintOfConstrainedElementFacade aceccef = new AConstrainedElementConstraintOfConstrainedElementFacade(p);
    	try {
			aceccef.getAConstrainedElementConstraintOfConstrainedElement().add(constrainedElement, c);
		} catch (ConstraintException e) {
			if (verbose) System.out.println("Error linking constraint and context");
			e.printStackTrace();
		}
        
		if (verbose) System.out.println("Constraint created successfully");

	        
		// following: ast generation for operation constraint (not required by spec).
		myAst.setStereotype(astStereotype);
		myAst.setName(astName);
		myAst.setExpression(astExpression);
	       
		return myAst;
	}
	public OclOperationContextDecl computeAstFor_AOperationContextDeclarationCs(
			OclOperationContextDecl myAst, Heritage nodeHrtg,
			List astContextName, OclOperationSignature astSignature,
			List astListConstraints) throws AttrEvalException {
		myAst.setContextName(astContextName);
        myAst.setSignature(astSignature);
        myAst.setConstraints(astListConstraints);        
        return myAst;
	}
	public OclOperationConstraintStereotype computeAstFor_APostOpConstraintStereotypeCs(
			Heritage nodeHrtg) throws AttrEvalException {
		return OclOperationConstraintStereotype.POST;
	}
	public OclOperationConstraintStereotype computeAstFor_APreOpConstraintStereotypeCs(
			Heritage nodeHrtg) throws AttrEvalException {
		return OclOperationConstraintStereotype.PRE;
	}
	public OclOperationConstraintStereotype computeAstFor_ABodyOpConstraintStereotypeCs(
			Heritage nodeHrtg) throws AttrEvalException {
		return OclOperationConstraintStereotype.BODY;
	}
	
	// Heritage for Operation Constraint (pre, post, body)
	public Heritage insideAOperationConstraintCs_computeHeritageFor_Expression(
			AOperationConstraintCs parent, POclExpressionCs child,
			Heritage parentHrtgCopy,
			OclOperationConstraintStereotype astStereotype, String astName)
			throws AttrEvalException {
		
		Heritage result = parentHrtgCopy;
        
		if ( astStereotype.equals(astStereotype.POST ) ||
             astStereotype.equals(astStereotype.BODY ) )
        {
            // add result variable only for specific types of operation constraint
            Variable resvar = result.getResultVariable();
            if ( resvar != null ) {                 
                WritableEnvironment wenv = result.getEnv().nestedEnvironment();
                try {
                    wenv.addElement(resvar.getName(), resvar, false);
                } catch ( DuplicateNameException dne ) {
                    rethrowDNE(dne, "addition of 'result' variable to environment");
                }
                result.setEnv(wenv);
            }             
        }
		
		if (astStereotype.equals(astStereotype.POST)) {
			result.setConstraintIsPostcondition(true);
		}
        return result;
	}
	public Heritage insideAOperationContextDeclarationCs_computeHeritageFor_Constraints(
			AOperationContextDeclarationCs parent,
			POperationConstraintCs child, Heritage parentHrtgCopy,
			List astContextName, OclOperationSignature astSignature)
			throws AttrEvalException {
		Heritage resultHeritage = parentHrtgCopy;
        Environment parentEnv = parentHrtgCopy.getEnv();
        
        // 1. lookup classifier                                                 
        // 2. lookup operation with matching signature inside classifier        
        // 3. if necessary: compare return types                                
        
        // check minimum length requirement of path name
        int contextNameSize = astContextName.size();
        if (  contextNameSize < 2 ) {
            throw new AttrEvalException("Need at least two components in pathname for operation context declaration");
        }
        
        // split path name into name for contextual classifier and name of 
        // constrained item inside contextual classifier 
        String operName = (String) astContextName.subList(contextNameSize-1, contextNameSize).get(0);
        String owningElementName = (String) astContextName.subList(0, contextNameSize-1).get(0);
        
        // lookup the contextual classifier's        
        UmlClass ctxCls = uf.findClassByName(owningElementName);
        if ( ctxCls == null ) {
            throw new AttrEvalException("Unknown element '" + owningElementName +
                "' specified as contextualClassifier for operation '" + operName +
                "' in current package");
        }
        
        // lookup operation     
        List params = astSignature.getFormalParameters();
        List paramTypes = astSignature.getFormalParameterTypes();
        
        Operation constrainedOperation = ut.lookupOperationWithOCLFormalParameter(ctxCls, operName, params);
        if ( constrainedOperation == null ) {
            throw new AttrEvalException("No matching operation named '" + 
                operName + "' in classifier '" + ctxCls.getName());
        }
        
        
        // compare return types
        Type specifiedReturnType = astSignature.getReturnType();
        Parameter returnParam = null;
        
        List opparams = constrainedOperation.getOwnedParameter();
        for (Iterator iterator = opparams.iterator(); iterator.hasNext();) {
			Parameter p = (Parameter) iterator.next();
			if (p.getDirection().equals(ParameterDirectionKindEnum.RETURN)) {
				returnParam = p;
			}
		}
        
        Type returnType = null;
        if ( returnParam != null ) {
            returnType = returnParam.getType();
            if (returnType == null) {
            	throw new AttrEvalException("Operation's return parameter's type must not be null");
            }
        }
        if (
               ( ( returnType == null ) && ( specifiedReturnType != null ) ) ||
               ( ( returnType != null ) && ( specifiedReturnType == null ) ) 
           ) 
        {
            throw new AttrEvalException("Return type mismatch on operation '" +
                    operName + "' in classifier '" + ctxCls.getName() + "'. " +
                    "Either both the specified and the actual operation must " +
                    "have a return type or none of them must specify one.");
        }
        // if both are null, comparison is already successful here, so don't    
        // perform further checks                                               
        if ( (returnType != null) || (specifiedReturnType != null) ) {
            if ( ! specifiedReturnType.getName().equals(returnType.getName()) ) {
                throw new AttrEvalException("Return type mismatch on operation '" +
                    operName + "' in classifier '" + ctxCls.getName() + "'. " +
                    "Constraint specifies '" + specifiedReturnType.getName() +
                    "', matching operation returns '" + returnType.getName() +
                    "'");
            }
        }        

        
        WritableEnvironment expressionEnv = parentEnv.nestedEnvironment();
        
        // Search at repository if exists a self variable with contextclass type
        Variable self = ut.findVariableByNameAndClass("self", ctxCls);
       	try {
       		if (self==null) {
       			// if it does not exist, we must to create it
       			self = createVariableForSelf(ctxCls);
       		}
       		// insert it into environment
       		expressionEnv.addElement("self", self, true);
        } catch ( DuplicateNameException dne ) {
        	rethrowDNE(dne, "contextual classifier 'self'");
        }
        

        WritableEnvironment innerEnv = expressionEnv.nestedEnvironment();
        Iterator it = params.iterator();

        while ( it.hasNext() ) {
            OclFormalParameter p = (OclFormalParameter) it.next();
            String name = p.getName();
            Type type = p.getType();
            Variable v = (Variable) factory.createNode("Variable");
            v.setName(name);
            v.setType(type);
            try {
                innerEnv.addElement(name, v, false);
            } catch ( DuplicateNameException dne ) {
                rethrowDNE(dne, "addition of formal operation parameters to environment");
            }
        }

        // v-- result variable is added to environment later, if we detect that the steretype is
        // 'post' or 'body'. We only put it into the *Heritage* now (not Environment)
        if ( returnParam != null ) {
            Variable vr = (Variable) factory.createNode("Variable");
            final String name = "result";            
            vr.setName(name);
            vr.setType(returnType);
            resultHeritage.setResultVariable(vr);
        }

        
        // update result heritage   
        resultHeritage.setEnv(innerEnv);
        resultHeritage.setContextualClassifier(ctxCls);
        resultHeritage.setConstrainedElement(constrainedOperation);
        return resultHeritage;
	}

	
	
	
	//************************************************************************
	//************************ Time Expressions ******************************
	//************************************************************************
	public OclTimeExp computeAstFor_AIsMarkedPreCs(Heritage nodeHrtg)
			throws AttrEvalException {
		return OclTimeExp.AT_PRE;
	}

	
	//************************************************************************
	//********************** Names and identifiers ***************************
	//************************************************************************
	public List computeAstFor_APathNameCs(Heritage nodeHrtg,
			List astListQualifier, String astName) throws AttrEvalException {
		List result = null;
        // qualifier is syntactically optional (check for presence)
        if ( astListQualifier != null ) {
            // qualifier exists
            astListQualifier.add(astName);
            result = astListQualifier;
        } else {
            result = (List) factory.createNode("List");
            result.add(astName);
        }
        return result;
	}

	
	//************************************************************************
	//********************** Language Constructions **************************
	//************************************************************************
	public IfExp computeAstFor_AIfExpCs(IfExp myAst, Heritage nodeHrtg,
			OclExpression astCondition, OclExpression astThenBranch,
			OclExpression astElseBranch) throws AttrEvalException {
		// Set the three OclExpressions into the ast node IfExp
		myAst.setCondition(astCondition);
		myAst.setThenExpression(astThenBranch);
		myAst.setElseExpression(astElseBranch);
		// Call typecheck to evaluate the correctness
		tc.typecheck(myAst);
		
		return myAst;
	}
	public LetExp computeAstFor_ALetExpCs(Heritage nodeHrtg,
			List astVariables, OclExpression astExpression)
			throws AttrEvalException {

		OclExpression in = null;
		
		LetExp let=null;
		LinkedList letlist = new LinkedList();
		
		for (Iterator iterator = astVariables.iterator(); iterator.hasNext();) {
			Variable var = (Variable) iterator.next();
			let = (LetExp) this.factory.createNode("LetExp");
			
			if (var.getType() == null) {
			    throw new AttrEvalException("type of initialized variable "+var.getName()+" must not be null");
			}
			
			if (var.getInitExpression() == null) {
				throw new AttrEvalException("initializer expression of initialized variable "+var.getName()+" not present");
			}
			
			let.setVariable(var);
			let.setName("Let "+var.getName());
			letlist.add(let);
		}
		
		// Link the let expressions with the In field
		int listsize = letlist.size();
		for (int i = 0; i<listsize; ++i) {
			let = (LetExp) letlist.get(i);
			if (i+1<listsize) {
				let.setIn((LetExp) letlist.get(i+1));
			}
			else {
				let.setIn(astExpression);
			}
		}
		LetExp myAst = (LetExp) letlist.get(0);
		tc.typecheck(myAst);
		
		return myAst;
	}
	
	//Heritage
	public Heritage insideALetExpCs_computeHeritageFor_Expression(
			ALetExpCs parent, PExpression child, Heritage parentHrtgCopy,
			List astVariables) throws AttrEvalException {
		Heritage resultHeritage = parentHrtgCopy;        
        WritableEnvironment childEnv = resultHeritage.getEnv().nestedEnvironment();
        resultHeritage.setEnv(childEnv);

        for (Iterator iterator = astVariables.iterator(); iterator.hasNext();) {
			Variable var = (Variable) iterator.next();
			try {
	            childEnv.addElement(var.getName(), var, false);
	        } catch ( DuplicateNameException dne ) {
	            rethrowDNE(dne, "let expression");
	        }
		}
        
        return resultHeritage;
	}
	
	
	
	//************************************************************************
	//********************** PropertyCall Expressions ************************
	//************************************************************************
			//+++++++++++++++++++++++++++++++++++++++++++
			// PROPERTYCALLEXP - PATH TIME GRAMMAR BRANCH
			//+++++++++++++++++++++++++++++++++++++++++++
	public OclExpression computeAstFor_APathTimePropertyCallExpCs(
			Heritage nodeHrtg, List astName, OclTimeExp astTime)
			throws AttrEvalException {
		Heritage hrtg = nodeHrtg;
        boolean contextIsPropertyPrimary = hrtg.isContextIsPropertyPrimary();
        boolean contextIsPropPostfixTail = hrtg.isContextIsPropPostfixTail();
        boolean contextIsIteratorVarDecl = hrtg.isContextIsIteratorVarDecl();
        
        // context can be: 
        //    Case 1: Property Primary (self, attribute, ...)
        //      OR
        //    Case 2: Postfix call (expressions after arrow or point operators)
        if (!(contextIsPropertyPrimary ^ contextIsPropPostfixTail)) {
           throw new AttrEvalException("Internal error: property_call_exp_cs{path_time} must be used either in context " + 
            "property_primary or prop_postfix_tail");
        }
        
        // Case 1
        if ( contextIsPropertyPrimary ) {
        	if (contextIsIteratorVarDecl) {
        		// Special method for IteratorExp
        		return computeAstFor_APathTimePropertyCallExpCs_inContextIteratorVarDecl(nodeHrtg, astName, astTime);
        	}
        	else {
        		return computeAstFor_APathTimePropertyCallExpCs_inContextPropertyPrimary(nodeHrtg, astName, astTime);
        	}
        }
        // Case 2
        if ( contextIsPropPostfixTail ) {
            return computeAstFor_APathTimePropertyCallExpCs_inContextPropPostfixTail(nodeHrtg, astName, astTime);
        }
        
        throw new AttrEvalException("context of alternative path_time of property_call_exp_cs must always be either propert_primary or postfix_tail");
	}
	// Case 1 inContextPropertyPrimary
	public OclExpression computeAstFor_APathTimePropertyCallExpCs_inContextPropertyPrimary(
			Heritage nodeHrtg, List astName, OclTimeExp astTime) 
			throws AttrEvalException {

		Heritage hrtg = nodeHrtg;
        Environment nodeEnv = hrtg.getEnv();
        int numNameElements = astName.size();
        NamedElement ne = null;
        boolean contextIsPostcondition = hrtg.isConstraintIsPostcondition();
		
		String nameHead;
		UmlClass ctxCls = (UmlClass) nodeHrtg.getContextualClassifier();
		
	    // Actually, we expect that astName only contains a simple name (one string element)
        if ( numNameElements == 1 ) {
        	// Obtain the string element
            nameHead = (String) astName.get(0);
            
            // CASE STUDY
            // 1- self (e.g. "self".property ...)
            // 2- Contextual Classifier (e.g. "Class".allInstances() -> ...)
            // 3- Another Class different than Contextual Classifier ( e.g. ... .oclIsTypeOf("Class")... )
            // 4- Property (Simple Attribute, e.g. "age = 3", or member of a binary association without AssociationClass)
            // 5- AssociationClass
            // 6- Class name in lower case
            // 7- Element obtained from Environment (e.g. ...forAll(p| "p".age = 4)... )
            
            //=============================================================
            // CASE 0. check for DataType name
            DataType dt = uf.findDataTypeByName(nameHead);
            if (dt != null) {
            	// create TypeExp
                TypeExp te = (TypeExp) this.factory.createNode("TypeExp");
                te.setReferredType(dt);
                te.setType(dt);
                te.setName(dt.getName());
                if (astTime != null) {
                	throw new AttrEvalException("Time operation @pre not allowed here");
                }
                return te;
            }
            
            //=============================================================
            // CASE 1. check for self case -> return VariableExp
            if (nameHead.equals("self")) {
                ne = nodeEnv.lookup(nameHead);
                if ( ne != null ) {
                    Element me = ne.getReferredElement();
                    if ( me instanceof Variable ) {
                    	// create VariableExp
                        Variable vardecl = (Variable) me;
                        VariableExp varexp = (VariableExp) factory.createNode("VariableExp");
                        varexp.setReferredVariable(vardecl);
                        varexp.setType(vardecl.getType());
                        varexp.setName("self");
                        if (verbose) System.out.println("Return VariableExp self");
                        return this.computeTimeExp(varexp, contextIsPostcondition, astTime);
                        //return varexp;
                    }
                }
                else throw new AttrEvalException("Internal error: self variable not found");
            } // end CASE 1
            
            //=============================================================
            // CASE 2. check for contextual class -> return Variable Exp
            if (nameHead.equals(ctxCls.getName())) {
            	VariableExp ve;
            	Variable v = null;
            	// Firstly, we search in Environment
            	ne = nodeEnv.lookup(nameHead);
                if ( ne != null ) {
                	Element me = ne.getReferredElement();
                	if ( me instanceof Variable) {
                		v = (Variable) me;
                		if (verbose) System.out.println("Variable context has been got from Environment");
                	}
                }// if the search has no results -> create a new Variable and add it into the Environment
                else {
                	v = (Variable) factory.createNode("Variable");
                	v.setName("contextual_classifier");
                	v.setType(ctxCls);
                	if (verbose) System.out.println("Context Variable was created successfully");
                	WritableEnvironment expressionEnv = (WritableEnvironment) nodeEnv;
                	try {
						expressionEnv.addElement(ctxCls.getName(), v, true);
					} catch (DuplicateNameException e) {
						throw new AttrEvalException("Error adding Variable into Environment");
					}
                }
                ve = (VariableExp) factory.createNode("VariableExp");
               	ve.setReferredVariable(v);
               	ve.setType(ctxCls);
               	ve.setName(ctxCls.getName());
               	if (verbose) System.out.println("Return VariableExp context class");
               	if (astTime != null) {
                	throw new AttrEvalException("Time operation @pre not allowed here");
                }
               	return ve;
            } // end CASE 2
            
            //=============================================================
            // CASE 3. check for another class distinct than contextual one
            UmlClass uc = uf.findClassByName(nameHead);
            if (uc != null) {
            	// VariableExp for operations whit type argument
            	VariableExp ve = (VariableExp) factory.createNode("VariableExp");
           		Variable v = (Variable) factory.createNode("Variable");
           		v.setName(uc.getName());
           		v.setType(uc);
           		ve.setName(uc.getName());
               	ve.setReferredVariable(v);
               	ve.setType(uc);
               	if (astTime != null) {
                	throw new AttrEvalException("Time operation @pre not allowed here");
                }
               	return ve;
            } // end CASE 3
            
            // Instructions only if context is iteratorexp without explicit iterator variables
            // Self Variable is always defined here. We can obtain it directly.
    		VariableExp self = ut.obtainVariableExpForSelf(nodeEnv);
    		
    		if (hrtg.isContextIsIteratorExp()) {
        		if (hrtg.isLacksExplicitIteratorVariables()) {
        			Variable implicit = hrtg.getImplicitIteratorVariable();
        			ctxCls = (UmlClass) implicit.getType();
        			VariableExp vaux = (VariableExp) this.factory.createNode("VariableExp");
        			vaux.setReferredVariable(implicit);
        			vaux.setType(implicit.getType());
        			vaux.setName("");
        			self = vaux;
        		}
        	}
    		
            //=============================================================
            // CASE 4. check for property (simple attribute or associationEnd) -> return PropertyCallExp
            Property prop = uf.findPropertyByName(nameHead);
            if ( prop != null ) {      	      	
            // Property exists!
        
            	// CASE 4.1. Property of contextual class
            	if (ut.propertyIsOfClass(prop, ctxCls)) {
            		prop = ut.findPropertyByNameAndClass(nameHead, ctxCls);
            		if (verbose) System.out.println("Property found for "+nameHead);
	
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(self, prop);
            		tc.typecheck(pce);
            	
            		if (verbose) System.out.println("PropertyCallExp was created successfully");
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
            	} // end CASE 4.1.
                
            	// CASE 4.2. Property is member of a binary association without AssociationClass-> return PropertyCallExp
            	if (ut.propertyIsAssociationEnd(prop)) {
                	UmlClass source = ctxCls;
                	// check connection between source and destiny
                	if (ut.existsConnection(source, prop)) {
                		
                		ut.checkCorrectNavigation(source, prop);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(self, prop);
                		tc.typecheck(pce);
                		
                		if (verbose) System.out.println("PropertyCallExp was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
                	}
                	// else
                	throw new AttrEvalException(nameHead +" is not a member of a binary association without association " +
                			"class between " + ctxCls.getName()+" and "+prop.getType().getName());	
                }// end CASE 4.2.
            	
            	// Property exists, but it does not belong to Context Class
            	throw new AttrEvalException(nameHead+ " does not belong to "+ctxCls.getName());
            }// end CASE 4
            
            //=============================================================
            // CASE 5. check for AssociationClass (first char in lower case)
            AssociationClass ac = ut.findAssociationClassByName(nameHead);
            if (ac != null) {
            	if (verbose) System.out.println("AssociationClass name:"+ ac.getName());
            	
            	ut.throwHelpOfCorrectCallWithQualifier(ac);
            	
            	//check connection between source and destiny
            	UmlClass source = ctxCls;
            	if (ut.existsConnection(source, ac)) {
            		
            		if (ut.connectionWithPCE(ctxCls, ac)) {
               			Property proper = ut.getPropertyOfConnection(ctxCls, ac); 
               			
               			ut.checkCorrectNavigation(ctxCls, proper);
               			
               			PropertyCallExp pce = ut.createPCEforPropertyOfClass(self, proper);
                		tc.typecheck(pce);
                		
                		if (verbose) System.out.println("PropertyCallExp for "+nameHead+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
               		}
            		else {
            			AssociationClassCallExp acce = ut.createACCEforNavigation(self, ac);
            			if (verbose) System.out.println("AssociationClassCallExp was created successfully");
            			return this.computeTimeExp(acce, contextIsPostcondition, astTime);
            		}
            	}
            	if (!(source instanceof AssociationClass)) {
            		throw new AttrEvalException(nameHead +" is not an AssociationClass with navegability from " + ctxCls.getName());
            	}
            }// end CASE 5
            
            //=============================================================
            // CASE 6. the element nameHead is a class name in lower case
            // First, convert name into Name (upper case of the first character) in order to find the class
    		String Name = ut.convertToUpper(nameHead);
    		UmlClass destiny = uf.findClassByName(Name);
    		if (destiny != null) {
    			// check connection between source and destiny
    			UmlClass source = ctxCls;
    			if (ut.existsConnection(source, destiny)) {
    				
            		Property p = ut.findPropertyOfDestinyBySource(source, destiny);
            		
            		ut.checkCorrectNavigation(source, p);
            		
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(self, p);
            		
            		if (verbose) System.out.println("PropertyCallExp was created successfully");
            		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
    			}
    		}// end CASE 6
            
            //=============================================================
            // CASE 7. the element nameHead is in the environment
            ne = nodeEnv.lookup(nameHead);
            if (ne!=null) {
            	Variable v = (Variable) ne.getReferredElement();
            	VariableExp vex = (VariableExp) factory.createNode("VariableExp");
            	vex.setName(nameHead);
            	vex.setReferredVariable(v);
            	vex.setType(v.getType());
            	return this.computeTimeExp(vex, contextIsPostcondition, astTime);
            	//return vex;
            }// end CASE 7
            
            //=============================================================
            // CASE 8. in a postcondition, the element does not exist previously
            if (hrtg.isConstraintIsPostcondition()) {
            	if (astTime != null) {
                	throw new AttrEvalException("Time operation @pre not allowed here");
                }
            	VariableExp vex = (VariableExp) factory.createNode("VariableExp");
            	vex.setName(nameHead);
            	Variable v = (Variable) factory.createNode("Variable");
            	v.setName(nameHead);
            	Type t = (AnyType)factory.createNode("AnyType");
            	t.setName("AnyType");
            	v.setType(t);
            	vex.setReferredVariable(v);
            	vex.setType(t);
            	WritableEnvironment expressionEnv = (WritableEnvironment) nodeEnv;
            	try {
					expressionEnv.addElement(nameHead, v, true);
				} catch (DuplicateNameException e) {
					throw new AttrEvalException("Error adding Variable into Environment");
				}
            	return vex;
            }
            
            
            //finally
            throw new AttrEvalException("Element "+getCurrentToken().getText()+" not exists in the model or has " +
            		"been used incorrectly",getCurrentToken());
        
        }// end numNameElements == 1
        
        if (numNameElements == 2) {
        	nameHead = (String) astName.get(0);
        	String litname = (String) astName.get(1);
        	EnumLiteralExp myAst = (EnumLiteralExp) this.factory.createNode("EnumLiteralExp");
        	return this.computeAstFor_AEnumLiteralExpCs(myAst, nodeHrtg, nameHead, litname);
        }
          
		throw new AttrEvalException("More than two elements is not allowed here to construct path names");
	}
	// Case 2 inContextPropPostfixTail
	public OclExpression computeAstFor_APathTimePropertyCallExpCs_inContextPropPostfixTail(
			Heritage nodeHrtgCopy, List astName, OclTimeExp astTime) 
			throws AttrEvalException {
		
		Heritage hrtg = nodeHrtgCopy;
		Environment nodeEnv = hrtg.getEnv();
		boolean contextIsPostcondition = hrtg.isConstraintIsPostcondition();
        
        int numNameElements = astName.size();
        if ( numNameElements > 1 ) {
            throw new AttrEvalException("PathName with more than one name element not allowed here");
        }        
        OclExpression source = hrtg.getCurrentSourceExpression();
        if (source == null) { 
        	throw new AttrEvalException("Current context requires source expression to be set; found java null value");
        }
        
        String name = (String) astName.get(0);
        UmlClass ctxCls = (UmlClass) hrtg.getContextualClassifier();
        
        //=============================================================
        // CASE -1. if source is a TypeExp, throw exception
        if (source instanceof TypeExp) 
        	throw new AttrEvalException("TypeExp not allowed to use in a Property Call Expression context");
        
        //=============================================================
        // CASE 0. source type is a TupleType (access an attribute)
        if (source.getType() instanceof TupleType) {
        	TupleType tt = (TupleType) source.getType();
        	Property p = ut.findAttributeOfTupleTypeByName(tt, name);
        	if (p!=null) {
        		PropertyCallExp pce = (PropertyCallExp) this.factory.createNode("PropertyCallExp");
        		pce.setSource(source);
        		pce.setReferredProperty(p);
        		pce.setType(p.getType());
        		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
        		//return pce;
        	}
        	else {
        		throw new AttrEvalException(name+" is not a member of "+source.getName());
        	}
        }
        
        
        //=============================================================
        // CASE 1. source expression is a VariableExp
        if (source instanceof VariableExp) {
        	if (verbose) System.out.println("Source is a VariableExp");
        	VariableExp ve = (VariableExp) source;
        	Variable v = ve.getReferredVariable();
        	if (hrtg.isConstraintIsPostcondition() && v.getType() instanceof AnyType) { 
        		// we are in the case of a postcondition with an element that has no type
        		throw new AttrEvalException(ve.getName()+" must be used previously with oclIsNew() operation");
        	}
        	
        	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        	// CASE 1.1. Variable is self
        	if (v.getName().equals("self")) {
        		//******************************************************
        		// Case A: Name is a Property
        		Property prop = uf.findPropertyByName(name);
                if ( prop != null ) {
                	// Property exists!
                	
                	// CASE 1.1.1. Property of contextual class
                	if (ut.propertyIsOfClass(prop, ctxCls)) {
                		prop = ut.findPropertyByNameAndClass(name, ctxCls);
                		if (verbose) System.out.println("Property found for "+name);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, prop);
                		if (verbose) System.out.println(pce.getType().getName());
                		tc.typecheck(pce);
                	
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
                	} // end CASE 1.1.1.
                    
                	// CASE 1.1.2. Property is association end
                	if (ut.propertyIsAssociationEnd(prop)) {
                    	// check connection between context class and destiny
                    	if (ut.existsConnection(ctxCls, prop)) {
                    		
                    		ut.checkCorrectNavigation(ctxCls, prop);
                    		
                    		PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, prop);
                    		tc.typecheck(pce);
                    		
                    		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                    		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                    		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                    		//return pce; // return as an OclExpression the PropertyCallExp
                    	}
                    	// else
                    	throw new AttrEvalException(name +" is not a member of a binary association without association class between " +
                    			ctxCls.getName()+" and "+prop.getType().getName());
                    }// end CASE 1.1.2.
                	
                	// Property exists, but it does not belong to Context Class
                	// or Name references a class with the same name as a property
                	if (uf.findClassByName(ut.convertToUpper(name)) == null) {
                	   throw new AttrEvalException(name+ " does not belong to "+ctxCls.getName());
                	}
                }// end Case A
                
                //******************************************************
                // Case B: Name is an AssociationClass
                AssociationClass ac = ut.findAssociationClassByName(name);
                if (ac != null) {
                	if (verbose) System.out.println("AssociationClass name:"+ ac.getName());
                    	
                	ut.throwHelpOfCorrectCallWithQualifier(ac);
                	
                   	//check connection between source and destiny
                   	if (ut.existsConnection(ctxCls, ac)) {
                  		// Self Variable ve. We can use it directly.
                   		if (ut.connectionWithPCE(ctxCls, ac)) {
                   			Property proper = ut.getPropertyOfConnection(ctxCls, ac); 
                   			
                   			ut.checkCorrectNavigation(ctxCls, proper);
                   			
                   			PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, proper);
                    		tc.typecheck(pce);
                    		
                    		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                    		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                    		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                    		//return pce; // return as an OclExpression the PropertyCallExp
                   		}
                   		else {
                   			AssociationClassCallExp acce = ut.createACCEforNavigation(ve, ac);
                   			if (verbose) System.out.println("AssociationClassCallExp was created successfully");
                   			return this.computeTimeExp(acce, contextIsPostcondition, astTime);
                   			//return acce; // return as an OclExpression the AssociationClassCallExp
                   		}
                   	}	
                    if (! (ctxCls instanceof AssociationClass)) {
                    	throw new AttrEvalException(name +" is not an AssociationClass with navegability from " + ctxCls.getName());
                    }
                }// end Case B
              
                //******************************************************
                // Case C: Name is a Class name in lower case
                // First, convert name into Name (upper case of the first character) in order to find the class
        		String Name = ut.convertToUpper(name);
        		UmlClass destiny = uf.findClassByName(Name);
        		if (destiny != null) {
        			// check connection between source and destiny
        			UmlClass sourceClass = ctxCls;
        			if (ut.existsConnection(sourceClass, destiny)) {
                		Property p = ut.findPropertyOfDestinyBySource(sourceClass, destiny);
                		
                		ut.checkCorrectNavigation(sourceClass, p);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, p);
                		
                		if (verbose) System.out.println("PropertyCallExp was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
        			}
        		}// end CASE C
                
                // Element with name does not exist
                throw new AttrEvalException("Element "+getCurrentToken().getText()+" not exists in the model or has " +
                    		"been used incorrectly",getCurrentToken());
            }// end CASE 1.1
            
        	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        	// CASE 1.2. Variable is an iterator variable
        	if (hrtg.isContextIsIteratorExp() || true) { // Is this condition needed? I don't think so
        		if (nodeEnv.lookup(ve.getName())!=null) {
        			
        			//******************************************************
        			// Case A: Name is a Property
        			Property prop = uf.findPropertyByName(name);
                    if ( prop != null ) {
                    	// Property exists!
                    	
                    	// CASE 1.2.1. Property of iterator class
                    	if (ut.propertyIsOfClass(prop, (UmlClass) v.getType())) {
                    		if (verbose) System.out.println("Property found for "+name);
                    		prop = ut.findPropertyByNameAndClass(name, (UmlClass) v.getType());
                    		PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, prop);
                    		tc.typecheck(pce);
                    	
                    		if (verbose) System.out.println("PropertyCallExp in IteratorExp for "+name+" context was created successfully");
                    		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                    		//return pce; // return as an OclExpression the PropertyCallExp
                    	} // end CASE 1.2.1.
                    	
                    	// CASE 1.2.2. Property is association end
                    	if (ut.propertyIsAssociationEnd(prop)) {
                        	// check connection between context class and destiny
                        	if (ut.existsConnection((UmlClass) ve.getType(), prop)) {
                        		
                        		ut.checkCorrectNavigation((UmlClass) ve.getType(), prop);
                        		
                        		PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, prop);
                        		tc.typecheck(pce);
                        		
                        		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                        		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                        		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                        		//return pce; // return as an OclExpression the PropertyCallExp
                        	}
                        	// else
                        	throw new AttrEvalException(name +" is not a member of a binary association without AssociationClass between " +
                        			ve.getType().getName()+" and "+prop.getType().getName());	
                        }// end CASE 1.2.2.
                    	
                    	// Property exists, but it does not belong to the variable class
                    	// or Name references a class with the same name as a property
                    	if (uf.findClassByName(ut.convertToUpper(name))==null)
                    		throw new AttrEvalException(name+ " does not belong to "+v.getType().getName());	
            		}// end Case A

                    //******************************************************
                    // Case B: Name is an AssociationClass
                    AssociationClass ac = ut.findAssociationClassByName(name);
                    if (ac != null) {
                    	if (verbose) System.out.println("AssociationClass name:"+ ac.getName());
                        	
                    	ut.throwHelpOfCorrectCallWithQualifier(ac);
                    	
                       	//check connection between source and destiny
                       	if (ut.existsConnection((UmlClass)v.getType(), ac)) {
                       		UmlClass sclass = (UmlClass)v.getType();
                       		if (ut.connectionWithPCE(sclass, ac)) {
                       			Property proper = ut.getPropertyOfConnection(sclass, ac); 
                   
                       			ut.checkCorrectNavigation(sclass, proper);
                       			
                       			PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, proper);
                        		tc.typecheck(pce);
                        		
                        		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                        		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                        		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                        		//return pce; // return as an OclExpression the PropertyCallExp
                       		}
                       		else {
                       			// Iterator Variable ve. We can use it to make the ACCExp
                       			AssociationClassCallExp acce = ut.createACCEforNavigation(ve, ac);
                       			if (verbose) System.out.println("AssociationClassCallExp was created successfully");
                       			return this.computeTimeExp(acce, contextIsPostcondition, astTime);
                       			//return acce; // return as an OclExpression the AssociationClassCallExp
                       		}
                       	}	
                       	if (! ((UmlClass)v.getType() instanceof AssociationClass)) {
                       		throw new AttrEvalException(name +" is not an AssociationClass with navegability from " + ((UmlClass)v.getType()).getName());
                       	}
                    }// end Case B
                  
                    //******************************************************
                    // Case C: Name is a Class name in lower case
                    // First, convert name into Name (upper case of the first character) in order to find the class
            		String Name = ut.convertToUpper(name);
            		UmlClass destiny = uf.findClassByName(Name);
            		if (destiny != null) {
            			// check connection between source and destiny
            			UmlClass sourceClass = (UmlClass) v.getType();
            			if (ut.existsConnection(sourceClass, destiny)) {
                    		Property p = ut.findPropertyOfDestinyBySource(sourceClass, destiny);
                    		
                    		ut.checkCorrectNavigation(sourceClass, p);
                    		
                    		PropertyCallExp pce = ut.createPCEforPropertyOfClass(ve, p);
                    		
                    		if (verbose) System.out.println("PropertyCallExp was created successfully");
                    		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                    		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                    		//return pce; // return as an OclExpression the PropertyCallExp
            			}
            		}// end CASE C
        		}
        	}// end CASE 1.2.
        	
        	throw new AttrEvalException("Incorrect expression: "+name+" not found in the UML model.");
        }// end CASE 1
        
        //=============================================================
        // CASE 2. check for OperationCallExp
        if (source instanceof OperationCallExp) {
        	
    		OperationCallExp oce = (OperationCallExp) source;
    		Type ocet = oce.getType();
    		while (ocet instanceof CollectionType) {
    			ocet = ((CollectionType) ocet).getElementType();
    		}
    		
    		// ********************************************************
        	// Case A: Name is a Property
        	Property prop = uf.findPropertyByName(name);
            if ( prop != null ) {
            	// Property exists!
            	if (!(ocet instanceof UmlClass)) throw new AttrEvalException("OperationCallExp does not have Class " +
            			"(or Collection of Classes) as a Type");
            	// Case 2.1. name is a Property of class
            	if (ut.propertyIsOfClass(prop, (UmlClass) ocet)) {
            		prop = ut.findPropertyByNameAndClass(name, (UmlClass)ocet);
            		if (verbose) System.out.println("Property found for "+name);
            		
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(oce, prop);
            		tc.typecheck(pce);
            		
            		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
            		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
            	}// end Case 2.1.
            	
            	// CASE 2.2. Property is association end
            	if (ut.propertyIsAssociationEnd(prop)) {
                	// check connection between context class and destiny
            		
                	if (ut.existsConnection((UmlClass) ocet, prop)) {
                		
                		ut.checkCorrectNavigation((UmlClass) ocet, prop);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(oce, prop);
                		tc.typecheck(pce);
                    	
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.computeResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
                	}
                	// else
                	throw new AttrEvalException(name +" is not a member of a binary association without AssociationClass between " +
                			ocet.getName()+" and "+prop.getType().getName());	
                }// end CASE 3.2.

            	// Property exists, but it does not belong to the class
            	// or Name references a class with the same name as a property
            	if (uf.findClassByName(ut.convertToUpper(name))==null) {
            		throw new AttrEvalException(name+ " does not belong to "+oce.getName());
            	}

            }// end Case A
            
            //******************************************************
            // Case B: Name is an AssociationClass
            AssociationClass ac = ut.findAssociationClassByName(name);
            if (ac != null) {
            	if (verbose) System.out.println("AssociationClass name:"+ ac.getName());
                	
            	ut.throwHelpOfCorrectCallWithQualifier(ac);
            	
               	//check connection between source and destiny
               	if (ut.existsConnection((UmlClass) ocet, ac)) {
              		
               		if (ut.connectionWithPCE((UmlClass) ocet, ac)) {
               			Property proper = ut.getPropertyOfConnection((UmlClass)ocet, ac); 
               			
               			ut.checkCorrectNavigation((UmlClass) ocet, proper);
               			
               			PropertyCallExp pce = ut.createPCEforPropertyOfClass(oce, proper);
                		tc.typecheck(pce);
                		
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
               		}
               		else {
               			AssociationClassCallExp acce = ut.createACCEforNavigation(oce, ac);
               			if (verbose) System.out.println("AssociationClassCallExp was created successfully");
               			return this.computeTimeExp(acce, contextIsPostcondition, astTime);
               			//return acce; // return as an OclExpression the AssociationClassCallExp
               		}
               	}	
               	if (! (ocet instanceof AssociationClass)) {
               		throw new AttrEvalException(name +" is not an AssociationClass with navegability from " + ocet.getName());
               	}
            }// end Case B
            
            //******************************************************
            // Case C: Name is a Class name in lower case
            // First, convert name into Name (upper case of the first character) in order to find the class
    		String Name = ut.convertToUpper(name);
    		UmlClass destiny = uf.findClassByName(Name);
    		if (destiny != null) {
    			// check connection between source and destiny
    			UmlClass sourceClass = (UmlClass) ocet;
    			if (ut.existsConnection(sourceClass, destiny)) {
            		Property p = ut.findPropertyOfDestinyBySource(sourceClass, destiny);
            		
            		ut.checkCorrectNavigation(sourceClass, p);
            		
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(oce, p);
	
            		if (verbose) System.out.println("PropertyCallExp was created successfully");
            		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
    			}
    		}// end CASE C
    
        	throw new AttrEvalException("OperationCallExp ("+oce.getReferredOperation().getName()+") cannot be used as source here");
        }// end CASE 2
        
      //=============================================================
        // CASE 3. check for IteratorExp or IterateExp
        if (source instanceof IteratorExp || source instanceof IterateExp) {
        	
        	LoopExp loop = null;
        	if (source instanceof IteratorExp) loop = (IteratorExp) source;
        	if (source instanceof IterateExp)  loop = (IterateExp) source;
        	
    		Type loopt = loop.getType();
    		while (loopt instanceof CollectionType) {
    			loopt = ((CollectionType) loopt).getElementType();
    		}
    		
    		// ********************************************************
        	// Case A: Name is a Property
        	Property prop = uf.findPropertyByName(name);
            if ( prop != null ) {
            	// Property exists!
            	if (!(loopt instanceof UmlClass)) throw new AttrEvalException("OperationCallExp does not have Class " +
            			"(or Collection of Classes) as a Type");
            	// Case 2.1. name is a Property of class
            	if (ut.propertyIsOfClass(prop, (UmlClass) loopt)) {
            		prop = ut.findPropertyByNameAndClass(name, (UmlClass)loopt);
            		if (verbose) System.out.println("Property found for "+name);
            		
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(loop, prop);
            		tc.typecheck(pce);
            		
            		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
            		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
            	}// end Case 2.1.
            	
            	// CASE 2.2. Property is association end
            	if (ut.propertyIsAssociationEnd(prop)) {
                	// check connection between context class and destiny
            		
                	if (ut.existsConnection((UmlClass) loopt, prop)) {
                		
                		ut.checkCorrectNavigation((UmlClass) loopt, prop);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(loop, prop);
                		tc.typecheck(pce);
                    	
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.computeResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
                	}
                	// else
                	throw new AttrEvalException(name +" is not a member of a binary association without AssociationClass between " +
                			loopt.getName()+" and "+prop.getType().getName());	
                }// end CASE 3.2.

            	// Property exists, but it does not belong to the class
            	// or Name references a class with the same name as a property
            	if (uf.findClassByName(ut.convertToUpper(name))==null) {
            		throw new AttrEvalException(name+ " does not belong to "+loopt.getName());
            	}

            }// end Case A
            
            //******************************************************
            // Case B: Name is an AssociationClass
            AssociationClass ac = ut.findAssociationClassByName(name);
            if (ac != null) {
            	if (verbose) System.out.println("AssociationClass name:"+ ac.getName());
                	
            	ut.throwHelpOfCorrectCallWithQualifier(ac);
            	
               	//check connection between source and destiny
               	if (ut.existsConnection((UmlClass) loopt, ac)) {
              		
               		if (ut.connectionWithPCE((UmlClass) loopt, ac)) {
               			Property proper = ut.getPropertyOfConnection((UmlClass)loopt, ac); 
               			
               			ut.checkCorrectNavigation((UmlClass) loopt, proper);
               			
               			PropertyCallExp pce = ut.createPCEforPropertyOfClass(loop, proper);
                		tc.typecheck(pce);
                		
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
               		}
               		else {
               			AssociationClassCallExp acce = ut.createACCEforNavigation(loop, ac);
               			if (verbose) System.out.println("AssociationClassCallExp was created successfully");
               			return this.computeTimeExp(acce, contextIsPostcondition, astTime);
               			//return acce; // return as an OclExpression the AssociationClassCallExp
               		}
               	}	
               	if (! (loopt instanceof AssociationClass)) {
               		throw new AttrEvalException(name +" is not an AssociationClass with navegability from " + loopt.getName());
               	}
            }// end Case B
            
            //******************************************************
            // Case C: Name is a Class name in lower case
            // First, convert name into Name (upper case of the first character) in order to find the class
    		String Name = ut.convertToUpper(name);
    		UmlClass destiny = uf.findClassByName(Name);
    		if (destiny != null) {
    			// check connection between source and destiny
    			UmlClass sourceClass = (UmlClass) loopt;
    			if (ut.existsConnection(sourceClass, destiny)) {
            		Property p = ut.findPropertyOfDestinyBySource(sourceClass, destiny);
            		
            		ut.checkCorrectNavigation(sourceClass, p);
            		
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(loop, p);
	
            		if (verbose) System.out.println("PropertyCallExp was created successfully");
            		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
    			}
    		}// end CASE C
    
        }// end CASE 3
        
        //=============================================================
        // CASE 4. check for PropertyCallExp
        if (source instanceof PropertyCallExp) {
        	if (verbose) System.out.println("PCE SOURCE");
        	PropertyCallExp sourcepce = (PropertyCallExp) source;
        	Property propsource = sourcepce.getReferredProperty();
        	
        	if (ut.propertyIsAssociationEnd(propsource)) {
        		//*****************************************************
        		// Case A: Name is a Property
        		Property prop = uf.findPropertyByName(name);
                if ( prop != null ) {
                	// Property exists!
                	
                	// CASE 4.1. name is a Property of class
                	if (ut.propertyIsOfClass(prop, (UmlClass) propsource.getType())) {
                		prop = ut.findPropertyByNameAndClass(name, (UmlClass) propsource.getType());
                		if (verbose) System.out.println("Property found for "+name);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourcepce, prop);
                		tc.typecheck(pce);
                		
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
                	} // end CASE 4.1.
                	
                	// CASE 4.2. Property is association end
                	if (ut.propertyIsAssociationEnd(prop)) {
                    	// check connection between context class and destiny
                		if (ut.computeResultTypeOfNavigation(sourcepce) == ut.ATTRIBUTE) {
                			throw new AttrEvalException("Attribute of class cannot be source of PropertyCallExp");
                		}
             
                    	if (ut.existsConnection((UmlClass) propsource.getType(), prop)) {
                    		
                    		ut.checkCorrectNavigation((UmlClass) propsource.getType(), prop);
                    		
                    		PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourcepce, prop);
                    		tc.typecheck(pce);
                        	
                    		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                    		if (verbose) System.out.println("Type of result to apply PCE: "+ut.computeResultTypeOfNavigation(pce));
                    		
                    		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                    		//return pce; // return as an OclExpression the PropertyCallExp
                    	}
                    	// else
                    	throw new AttrEvalException(name +" is not a member of a binary association without AssociationClass between " +
                    			propsource.getType().getName()+" and "+prop.getType().getName());	
                    }// end CASE 4.2.

                	// Property exists, but it does not belong to the class
                	// or Name references a class with the same name as a property
                	if (uf.findClassByName(ut.convertToUpper(name))==null) {
                		throw new AttrEvalException(name+ " does not belong to "+propsource.getType().getName());
                	}
                	                    	
        		}// end Case A
              
                //******************************************************
                // Case B: Name is an AssociationClass
                AssociationClass ac = ut.findAssociationClassByName(name);
                if (ac != null) {
                	if (verbose) System.out.println("AssociationClass name:"+ ac.getName());
                    	
                	ut.throwHelpOfCorrectCallWithQualifier(ac);
                	UmlClass sourceType = (UmlClass) propsource.getType();
                	
                   	//check connection between source and destiny
                   	if (ut.existsConnection((UmlClass) propsource.getType(), ac)) {
                  		
                   		if (ut.connectionWithPCE(sourceType, ac)) {
                   			Property proper = ut.getPropertyOfConnection(sourceType, ac); 
                   			
                   			ut.checkCorrectNavigation(sourceType, proper);
                   			
                   			PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourcepce, proper);
                    		tc.typecheck(pce);
                    		
                    		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                    		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                    		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                    		//return pce; // return as an OclExpression the PropertyCallExp
                   		}
                   		else {
                   			AssociationClassCallExp acce = ut.createACCEforNavigation(sourcepce, ac);
                   			if (verbose) System.out.println("AssociationClassCallExp was created successfully");
                   			return this.computeTimeExp(acce, contextIsPostcondition, astTime);
                   			//return acce; // return as an OclExpression the AssociationClassCallExp
                   		}
                   	}	
                   	if (! (sourceType instanceof AssociationClass)) {
                   		throw new AttrEvalException(name +" is not an AssociationClass with navegability from " + propsource.getType().getName());
                   	}
                }// end Case B
                
                //******************************************************
                // Case C: Name is a Class name in lower case
                // First, convert name into Name (upper case of the first character) in order to find the class
        		String Name = ut.convertToUpper(name);
        		UmlClass destiny = uf.findClassByName(Name);
        		if (destiny != null) {
        			// check connection between source and destiny
        			UmlClass sourceClass = (UmlClass) propsource.getType();
        			if (ut.existsConnection(sourceClass, destiny)) {
                		Property p = ut.findPropertyOfDestinyBySource(sourceClass, destiny);
                		
                		ut.checkCorrectNavigation(sourceClass, p);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourcepce, p);
		
                		if (verbose) System.out.println("PropertyCallExp was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
        			}
        		}// end CASE C
                
        	} // end IF source property is an association end
        	else { // source property is attribute of class
        		throw new AttrEvalException("PropertyCallExp with an attribute of class as a " +
        			"referred property cannot be source of another property");
        	}
        }// end CASE 4
        
        //=============================================================
        // CASE 5. check for AssociationClassCallExp       
        if (source instanceof AssociationClassCallExp) {
        	AssociationClassCallExp sourceacce = (AssociationClassCallExp) source;
        	AssociationClass currentclass = sourceacce.getReferredAssociationClass();
        	
        	//******************************************************
        	// Case A: name is a Property
        	Property prop = uf.findPropertyByName(name);
            if ( prop != null ) {
            	// Property exists!
            	
            	// CASE 5.1. name is a Property of contextual class
            	if (ut.propertyIsOfClass(prop, currentclass)) {
            		prop = ut.findPropertyByNameAndClass(name, currentclass);
            		if (verbose) System.out.println("Property found for "+name);
            		
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourceacce, prop);
            		tc.typecheck(pce);
            		
            		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
            		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
            		
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
            	} // end CASE 5.1.
            	
            	// CASE 5.2. Property is association end
            	if (ut.propertyIsAssociationEnd(prop)) {
                	if (ut.existsConnection(currentclass, prop)) {
                		
                		ut.checkCorrectNavigation(currentclass, prop);
                		
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourceacce, prop);
                		tc.typecheck(pce);
                    	
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.computeResultTypeOfNavigation(pce));
                		
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
                	}
                	// else
                	throw new AttrEvalException(name +" is not a member of a binary association without AssociationClass between " +
                			currentclass.getName()+" and "+prop.getType().getName());	
                }// end CASE 5.2.

            	// Property exists, but it does not belong to the class
            	// or Name references a class with the same name as a property
            	if (uf.findClassByName(ut.convertToUpper(name))==null) {
            		throw new AttrEvalException(name+ " does not belong to "+currentclass.getName());
            	}
            	                    	
    		}// end Case A
            
            //******************************************************
            // Case B: Name is an AssociationClass
            AssociationClass ac = ut.findAssociationClassByName(name);
            if (ac != null) {
            	if (verbose) System.out.println("AssociationClass name:"+ ac.getName());
                	
            	ut.throwHelpOfCorrectCallWithQualifier(ac);
            	
               	//check connection between source and destiny
               	if (ut.existsConnection(currentclass, ac)) {
           
               		if (ut.connectionWithPCE(currentclass, ac)) {
               			Property proper = ut.getPropertyOfConnection(currentclass, ac); 
               			
               			ut.checkCorrectNavigation(currentclass, proper);
               			
               			PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourceacce, proper);
                		tc.typecheck(pce);
                		
                		if (verbose) System.out.println("PropertyCallExp for "+name+" was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
                		//return pce; // return as an OclExpression the PropertyCallExp
               		}
               		else {
               			AssociationClassCallExp acce = ut.createACCEforNavigation(sourceacce, ac);
               			if (verbose) System.out.println("AssociationClassCallExp was created successfully");
               			return this.computeTimeExp(acce, contextIsPostcondition, astTime);
               			//return acce; // return as an OclExpression the AssociationClassCallExp
               		}
               	}
               	else { //Check connection between associationclass source and destiny when destiny is one of the 
               		   // members in the association with the associationclass source
               		if (ut.existsConnectionBetweenAssociationClasses(currentclass, ac)) {
               			Property p = ut.findPropertyOfDestinyBetweenAssociationClasses(currentclass, ac);
                		PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourceacce, p);
                		
                		if (verbose) System.out.println("PropertyCallExp was created successfully");
                		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
                		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
               		}
               	}

            }// end Case B
          
            //******************************************************
            // Case C: Name is a Class name in lower case
            // First, convert name into Name (upper case of the first character) in order to find the class
    		String Name = ut.convertToUpper(name);
    		UmlClass destiny = uf.findClassByName(Name);
    		if (destiny != null) {
    			// check connection between source and destiny
    			UmlClass sourceClass = currentclass;
    			if (ut.existsConnection(sourceClass, destiny)) {
            		Property p = ut.findPropertyOfDestinyBySource(sourceClass, destiny);
            		
            		ut.checkCorrectNavigation(sourceClass, p);
            		
            		PropertyCallExp pce = ut.createPCEforPropertyOfClass(sourceacce, p);
            		
            		if (verbose) System.out.println("PropertyCallExp was created successfully");
            		if (verbose) System.out.println("Type of result to apply PCE: "+ut.getNameOfResultTypeOfNavigation(pce));
            		return this.computeTimeExp(pce, contextIsPostcondition, astTime);
            		//return pce; // return as an OclExpression the PropertyCallExp
    			}
    		}// end CASE C
    		
        	
        }// end CASE 5
        
        throw new AttrEvalException("Incorrect usage of "+name);
        //return null;
	}
	// Case 3 inContextIteratorVarDecl
	public OclExpression computeAstFor_APathTimePropertyCallExpCs_inContextIteratorVarDecl(
			Heritage nodeHrtg, List astName, OclTimeExp astTime) 
			throws AttrEvalException {
		
		Heritage hrtg = nodeHrtg;
		boolean contextIsPostcondition = hrtg.isConstraintIsPostcondition();
        Environment nodeEnv = hrtg.getEnv();
        int numNameElements = astName.size();
        NamedElement ne = null;
        String nameHead;
        
        if (verbose) System.out.println("IteratorExp context: variable iterators declaration");
        
		if ( numNameElements == 1 ) {
            nameHead = (String) astName.get(0);
            
            // CASE 1. check for iterator variable -> return VariableExp
            ne = nodeEnv.lookup(nameHead);
            // if the search does not have results, we can use the name for the variable iterator
            // because it is not used yet.
            if ( ne == null ) {
                // create VariableExp
                Variable vardecl = (Variable) factory.createNode("Variable");
                vardecl.setName(nameHead);
                VariableExp varexp = (VariableExp) factory.createNode("VariableExp");
                varexp.setReferredVariable(vardecl);
                varexp.setName(nameHead);
                if (verbose) System.out.println("Return VariableExp iterator "+nameHead);
                return this.computeTimeExp(varexp, contextIsPostcondition, astTime);
                //return varexp;
            }
            else throw new AttrEvalException("Identifier "+nameHead+" is already declared. Use another different name.");
            // end CASE 1
		}
		throw new AttrEvalException("It is not allowed to use a pathname, please use a simplename");
	}
	
			//+++++++++++++++++++++++++++++++++++++++++++++++
			// PROPERTYCALLEXP - ARGUMENT LIST GRAMMAR BRANCH
			//+++++++++++++++++++++++++++++++++++++++++++++++
	// Operations in PropertyCallExp context, e.g. PCE.Operation(argument_list)
	public OclExpression computeAstFor_AArgListPropertyCallExpCs(
			Heritage nodeHrtg, List astName, OclTimeExp astTime,
			List astParameters) throws AttrEvalException {
		
		Heritage hrtg = nodeHrtg;
        //Environment nodeEnv = hrtg.getEnv();
        
        boolean contextIsPropertyPrimary = hrtg.isContextIsPropertyPrimary();
        boolean contextIsPropPostfixTail = hrtg.isContextIsPropPostfixTail();
        boolean contextIsOclOpWithTypeArg = isOclOpWithTypeArg(astName);
        boolean contextIsIteratorExp = hrtg.isContextIsIteratorExp();
        boolean contextIsPostcondition = hrtg.isConstraintIsPostcondition();
        
        // Check that we are in the correct context
        if  (!(contextIsPropertyPrimary ^ contextIsPropPostfixTail)) {
            throw new AttrEvalException("Internal error: property_call_exp_cs{arg_list} must be used either in context " + 
            "property_primary or prop_postfix_tail");
        }
        
        int numNameElements = astName.size();
        
        // This List must not be null (but could be empty)
        if (astParameters == null) {  throw new AttrEvalException("Internal error: parameter list " +
            "reference 'astParameters' for AArgListPropertyCallExpCs may not be null");
        }
        
        // CONTEXT 1: PropertyPrimary
        if ( contextIsPropertyPrimary ) {
        	if (contextIsOclOpWithTypeArg) {
        		if (numNameElements != 1) 
                	throw new AttrEvalException("only simple names allowed in this context but found path name with " + numNameElements + " elements");
        		if (contextIsIteratorExp) {
        			Variable source = hrtg.getImplicitIteratorVariable();
        			VariableExp vexp = (VariableExp) factory.createNode("VariableExp");
        			vexp.setName(source.getName());
        			vexp.setType(source.getType());
        			vexp.setReferredVariable(source);
        			String nameHead = (String) astName.get(0);
        			if (source == null) throw new AttrEvalException("Must use this operation with an explicit source in this context (IteratorExp)");
        			OperationCallExp oce = createOpWithTypeArg(nameHead, vexp, astParameters);
        			return this.computeTimeExp(oce, contextIsPostcondition, astTime);
        		}
        	}
        	throw new AttrEvalException("Only use the operations oclIsTypeOf and OclIsKindOf, " +
        			"with implicit source, in iterator expressions without explicit iterator variables.");
        }// end contextIsPropertyPrimary
        
        //CONTEXT 2: PropertyPostfixTail
        if ( contextIsPropPostfixTail ) {
            OclExpression source = hrtg.getCurrentSourceExpression();
            if (source == null)
                throw new AttrEvalException("source expression may not be null if contextIsPropPostfixTail inside current method");
        
            if (numNameElements != 1) 
            	throw new AttrEvalException("only simple names allowed in this context but found path name with " + numNameElements + " elements");
            
            String nameHead = (String) astName.get(0);
            
            // Case 2.1. ocl operation with type argument
            if ( contextIsOclOpWithTypeArg ) {
            	OperationCallExp oce = createOpWithTypeArg(nameHead, source, astParameters);
            	if (hrtg.isConstraintIsPostcondition()) {
            		if (oce.getSource() instanceof VariableExp) {
            			VariableExp ve = (VariableExp) oce.getSource();
            			NamedElement ne = hrtg.getEnv().lookup(ve.getReferredVariable().getName());
            			if (ne.getReferredElement() instanceof Variable) {
            				Variable v = (Variable) ne.getReferredElement();
            				if (v.getType() instanceof AnyType) {
            					v.setType(oce.getType());
            				}
            			}
            		}
            	}
            	return this.computeTimeExp(oce, contextIsPostcondition, astTime);
            }
            
            // Case 2.1.1 oclIsNew operation
            if (nameHead.equals("oclIsNew")) {
            	if (nodeHrtg.isConstraintIsPostcondition()) {
            		// oclIsNew(parameters) -> parameters is empty
                	if (!astParameters.isEmpty()) {
                		throw new AttrEvalException("oclIsNew operation must not have parameters");
                	}
                	if (source instanceof VariableExp) {
                    	if (verbose) System.out.println("Source is a VariableExp");
                    	
                    	VariableExp ve = (VariableExp) source;
                    	
                    	// We are in name.oclIsNew()
            			Operation op = this.ocl.findOclOperationByName("oclIsNew");
            			OperationCallExp oce = (OperationCallExp) factory.createNode("OperationCallExp");
            			oce.setSource(ve);
            			oce.setReferredOperation(op);
            			oce.setType(this.uf.findDataTypeByName("Boolean"));
            			return this.computeTimeExp(oce, contextIsPostcondition, astTime);
                	}
                	else throw new AttrEvalException("Source expression must be a VariableExp");
            	}
            	else throw new AttrEvalException("oclIsNew operation must only be used in a postcondition.");
            }
            
            // Case 2.2. allInstances operation
            if (nameHead.equals("allInstances")) {          	
               // allinstances(parameters) -> parameters is empty
            	if (!astParameters.isEmpty()) {
            		throw new AttrEvalException("allInstances operation must not have parameters");
            	}
            	if (source instanceof VariableExp) {
                	if (verbose) System.out.println("Source is a VariableExp");
                	
                	VariableExp ve = (VariableExp) source;
                	Variable v = ve.getReferredVariable();
                	
                	if (v.getType() instanceof UmlClass && !(v.getType() instanceof TupleType)) {
                		if (verbose) System.out.println("Variable is context class");
                		
                		if (ve.getName()!=null) {
                			if (!ve.getName().equals(v.getType().getName())) {
                				throw new AttrEvalException("Incorrect usage of allInstances operation.\nUsage: Class.allInstances()");
                			}
                		}
                		else {
                			throw new AttrEvalException("Incorrect usage of allInstances operation.\nUsage: Class.allInstances()");
                		}
                		
                		// We are in Class.allInstances()
        				Operation op = (Operation) factory.createNode("Operation");
        				op.setName("allInstances");
        				SetType set = (SetType) factory.createNode("SetType");
        				set.setName("Set("+v.getType().getName()+")"); 
        				set.setElementType(v.getType());
        				OperationCallExp oce = (OperationCallExp) factory.createNode("OperationCallExp");
        				oce.setSource(ve);
        				oce.setReferredOperation(op);
        				oce.setType(set);
        				return this.computeTimeExp(oce, contextIsPostcondition, astTime);
                	}
            	}
            	else throw new AttrEvalException("Source expression must be a VariableExp");
            }
            // Case 2.3. Integer, Real and String operations
            if (tc.isAdditionalOp(nameHead)) {
            	return createOCEforAdditionalOp(nameHead, source, astParameters);
            }
            
            // Case 2.4. Class operation
            Type tsource = ut.getCorrectType(source);
            if (tsource instanceof UmlClass && !(tsource instanceof TupleType)) {
            	Operation op = ut.lookupOperationWithRealParams((UmlClass)tsource, nameHead, astParameters);
            	if (op != null) { 
            		OperationCallExp oce = (OperationCallExp) factory.createNode("OperationCallExp");
            		oce.setSource(source);
            		oce.setReferredOperation(op);
            		oce.setType(this.ocl.getTypeOfBasicOperation(op));
            		// set the parameters (relation argument - parentCall)
            		if (!astParameters.isEmpty()) {
            			AArgumentParentCall aapc = this.apcf.getAArgumentParentCall();
            			for (Iterator iterator = astParameters.iterator(); iterator.hasNext();) {
            				OclExpression param = (OclExpression) iterator.next();
            				aapc.add(param, oce);
            			}
            		}
            		
            		return this.computeTimeExp(oce, contextIsPostcondition, astTime);
            	}
            	else throw new AttrEvalException("Error: Incorrect use of "+nameHead+" operation with" +
            			"Class "+tsource.getName()+". Also check the parameters.");
            }
            
            throw new AttrEvalException("Error: Incorrect use of "+nameHead+" operation. Sorry!");
        }// end: contextIsPropPostfixTail
        throw new AttrEvalException("Bad use of CallExp");
	}
			
	private OclExpression createOCEforAdditionalOp(String name,
			OclExpression source, List astParameters) throws AttrEvalException {
		Type t = null;
		Type st = tc.typecheck(source);
		if (astParameters.isEmpty()) {
			if (name.equals("floor") || name.equals("round")) {
				if (st.equals(uf.findDataTypeByName("Real"))) {
					t = uf.findDataTypeByName("Integer");
				}
				else throw new AttrEvalException("Error: source must be Real for "+name+" operation.");
			}
			else if (name.equals("abs")) {
				if (st.equals(uf.findDataTypeByName("Integer")) || st.equals(uf.findDataTypeByName("Real"))) {
					t = st;
				}
				else throw new AttrEvalException("Error: source must be Real or Integer for "+name+" operation.");
			}
			else if (name.equals("size")) {
				if (st.equals(uf.findDataTypeByName("String"))) {
					t = uf.findDataTypeByName("Integer");
				}
				else throw new AttrEvalException("Error: source must be String for "+name+" operation.");
			}
			else if (name.equals("toInteger")) {
				if (st.equals(uf.findDataTypeByName("String"))) {
					// Especial case
					if (source instanceof StringLiteralExp) {
						StringLiteralExp sle = (StringLiteralExp) source;
						String value = sle.getStringSymbol();
						try {
							int ival = Integer.parseInt(value);
						}
						catch(NumberFormatException nfe) {
							throw new AttrEvalException("Error: Number format incompatible for "+value+" to Integer.");
						}
					}
					t = uf.findDataTypeByName("Integer");
				}
				else throw new AttrEvalException("Error: source must be String for "+name+" operation.");
			}
			else if (name.equals("toReal")) {
				if (st.equals(uf.findDataTypeByName("String"))) {
					// Especial case
					if (source instanceof StringLiteralExp) {
						StringLiteralExp sle = (StringLiteralExp) source;
						String value = sle.getStringSymbol();
						try {
							float fval = Float.parseFloat(value);
						}
						catch(NumberFormatException nfe) {
							throw new AttrEvalException("Error: Number format incompatible for "+value+" to Integer.");
						}
					}
					t = uf.findDataTypeByName("Real");
				}
				else throw new AttrEvalException("Error: source must be String for "+name+" operation.");
			}
			else {
				throw new AttrEvalException("Error: Incorrect use of "+name+" as an operation.");
			}
		}
		else if (astParameters.size()==1) {
			OclExpression param = (OclExpression) astParameters.get(0);
			Type tparam = param.getType();
			
			if (name.equals("max") || name.equals("min")) {
				if (st.equals(uf.findDataTypeByName("Integer")) || st.equals(uf.findDataTypeByName("Real"))) {
					if (tparam.equals(st)) {
						t = st;
					}
					else throw new AttrEvalException("Error: Incompatibility of types for "
							+name+" operation. "+st.getName()+" and "+tparam.getName()+".");
				}
				else throw new AttrEvalException("Error: source must be Real or Integer for "+name+" operation.");
			}
			else if (name.equals("div") || name.equals("mod")) {
				Type integer = uf.findDataTypeByName("Integer");
				if (st.equals(integer)) {
					if (tparam.equals(integer)) {
						t = integer;
					}
					else throw new AttrEvalException("Error: Incompatibility of types for "
							+name+" operation. Integer and "+tparam.getName()+".");
				}
				else throw new AttrEvalException("Error: source must be Integer for "+name+" operation.");
			}
			else if (name.equals("concat")) {
				Type string = uf.findDataTypeByName("String");
				if (st.equals(string)) {
					if (tparam.equals(string)) {
						t = string;
					}
					else throw new AttrEvalException("Error: Incompatibility of types for "
							+name+" operation. String and "+tparam.getName()+".");
				}
				else throw new AttrEvalException("Error: source must be String for "+name+" operation.");
			}
			else {
				throw new AttrEvalException("Error: Incorrect use of "+name+" as an operation.");
			}
		}
		else if (astParameters.size()==2) {
			if (name.equals("substring")) {
				OclExpression param1 = (OclExpression) astParameters.get(1);
				Type tparam1 = param1.getType();
				OclExpression param2 = (OclExpression) astParameters.get(0);
				Type tparam2 = param2.getType();
				
				Type string = uf.findDataTypeByName("String");
				if (source.getType().equals(string)) {
					Type integer = uf.findDataTypeByName("Integer");
					if (!tparam1.equals(integer)) throw new AttrEvalException("Error: first parameter must be String for "+name+" operation.");
					if (!tparam2.equals(integer)) throw new AttrEvalException("Error: second parameter must be String for "+name+" operation.");
					t = string;
				}
				else throw new AttrEvalException("Error: source must be String for "+name+" operation.");
			}
			else {
				throw new AttrEvalException("Error: Incorrect use of "+name+" as an operation.");
			}
		}
		else {
			throw new AttrEvalException("Error: Incorrect use of "+name+" as an operation.");
		}
		
		OperationCallExp oce = (OperationCallExp) this.factory.createNode("OperationCallExp");
		Operation op = this.ocl.findOclOperationByName(name);
		oce.setReferredOperation(op);
		oce.setSource(source);
		oce.setType(t);
		// set the parameters (relation argument - parentCall)
		if (!astParameters.isEmpty()) {
			AArgumentParentCall aapc = this.apcf.getAArgumentParentCall();
			for (Iterator iterator = astParameters.iterator(); iterator
					.hasNext();) {
				OclExpression param = (OclExpression) iterator.next();
				aapc.add(param, oce);
			}
		}
		
		return oce;
	}

			//+++++++++++++++++++++++++++++++++++
			// PROPERTYCALLEXP - QUALIFIED BRANCH
			//+++++++++++++++++++++++++++++++++++
	public OclExpression computeAstFor_AQualifiedPropertyCallExpCs(
			Heritage nodeHrtg, List astName, List astQualifiers,
			OclTimeExp astTime) throws AttrEvalException {
		Heritage hrtg = nodeHrtg;
        //Environment nodeEnv = hrtg.getEnv();
        
        boolean contextIsPropertyPrimary = hrtg.isContextIsPropertyPrimary();
        boolean contextIsPropPostfixTail = hrtg.isContextIsPropPostfixTail();
        boolean contextIsPostcondition = hrtg.isConstraintIsPostcondition();
        
        // Check that we are in the correct context
        if  (!(contextIsPropertyPrimary ^ contextIsPropPostfixTail)) {
            throw new AttrEvalException("Internal error: property_call_exp_cs[qualifiers] must be used either in context " + 
            "property_primary or prop_postfix_tail");
        }
        
        int numNameElements = astName.size();
        
        if (numNameElements != 1) { 
        	throw new AttrEvalException("only simple names allowed in this context but found path name " +
                 "with " + numNameElements + " elements");
        }
        String name = (String) astName.get(0);
        
        if (astQualifiers.size()!=1) {
        	throw new AttrEvalException("Use one qualifier. Found: "+astQualifiers.size());
        }
        
        String qualifier = (String) astQualifiers.get(0);
        
        if (ut.isFirstCharUpper(name)) {
        	throw new AttrEvalException("Semantic error: Please use "+name+" with first character in lower case");
        }
        AssociationClass ac = uf.findAssociationClassByName(ut.convertToUpper(name));
        if (ac==null) {
        	throw new AttrEvalException("Semantic error: "+name+" is not an AssociationClass");
        }
        
        Property p = uf.findPropertyByName(qualifier);
        if (p==null) {
        	throw new AttrEvalException("Semantic error: "+qualifier+" is not a Property");
        }
        
        // This List must not be null
        if (astQualifiers == null) {  
        	throw new AttrEvalException("Internal error: qualifier list " +
            "reference 'astQualifiers' for AQualifierPropertyCallExpCs may not be null");
        }
        
        List members = ac.getMemberEnd();
        if (members.size()!=2) {
        	throw new AttrEvalException("Error: association "+ac.getName()+" must have only two " +
        			"members but it have "+members.size());
        }
        Type tmember1,tmember2;
        tmember1 = ((Property)members.get(0)).getType();
        tmember2 = ((Property)members.get(1)).getType();
        if (!tmember1.equals(tmember2)) {
        	throw new AttrEvalException("Semantic Error: association "+ac.getName()+" is not a reflexive " +
        			"association. Not use a qualifier call expression");
        }
        String nmember1, nmember2;
        nmember1 = ((Property)members.get(0)).getName();
        nmember2 = ((Property)members.get(1)).getName();
        if ( (!qualifier.equals(nmember1) && !qualifier.equals(nmember2))
          || (qualifier.equals(nmember1) && qualifier.equals(nmember2))
           )
        {
        	throw new AttrEvalException("Semantic Error: property "+qualifier+" is not a member of " +
        			"AssociationClass "+ac.getName());
        }
        
        OclExpression ast = null;
        
        // CONTEXT 1: PropertyPrimary
        if ( contextIsPropertyPrimary ) {
        	VariableExp self = ut.obtainVariableExpForSelf(hrtg.getEnv());
        	UmlClass selfClass = (UmlClass) self.getType();
        	if (ut.existsConnection(selfClass, ac)) {
        		AssociationClassCallExp acce = ut.createACCEforNavigation(self, ac);
        		ut.insertQualifier(acce, qualifier);
        		ast = acce;
        	}
        	else throw new AttrEvalException("Semantic Error: Not exists navegability from "+selfClass.getName()+" to "+ac.getName());
        }// end contextIsPropertyPrimary
	        
	    //CONTEXT 2: PropertyPostfixTail
        if ( contextIsPropPostfixTail ) {
        	OclExpression source = hrtg.getCurrentSourceExpression();
        	if (source == null) {
        		throw new AttrEvalException("source expression may not be null if contextIsPropPostfixTail inside current method");
        	}
        	Type tsource;
        	if (source instanceof PropertyCallExp) {
        		PropertyCallExp pcesource = (PropertyCallExp) source;
        		int rtype = ut.computeResultTypeOfNavigation(pcesource);
        		if (rtype == ut.CLASS || rtype == ut.SET_INDIVIDUAL) {
        			if (ut.existsConnection((UmlClass)pcesource.getType(), ac)) {
        				AssociationClassCallExp acce = ut.createACCEforNavigation(pcesource, ac);
        				ut.insertQualifier(acce, qualifier);
        				ast = acce;
        			}
        			else throw new AttrEvalException("Semantic Error: Not exists navegability " +
        					"from "+pcesource.getType().getName()+" to "+ac.getName());
        		}
        		else throw new AttrEvalException("Semantic Error: Source type for qualifier navegation " +
        				"must be an UmlClass, but it is"+ut.getNameOfResultTypeOfNavigation(pcesource));
        	}
        	else {
        		tsource = source.getType();
        		if (tsource instanceof UmlClass) {
        			if (ut.existsConnection((UmlClass)tsource, ac)) {
                		AssociationClassCallExp acce = ut.createACCEforNavigation(source, ac);
                		ut.insertQualifier(acce, qualifier);
                		ast = acce;
                	}
                	else throw new AttrEvalException("Semantic Error: Not exists navegability from "+tsource.getName()+" to "+ac.getName());
        		}
        		else throw new AttrEvalException("Semantic Error: Source type must be an UmlClass but it is "+tsource.getName());
        	}
        }
		//return ast;
        return this.computeTimeExp(ast, contextIsPostcondition, astTime);

	}
	public Heritage insideAQualifiedPropertyCallExpCs_computeHeritageFor_Qualifiers(
			AQualifiedPropertyCallExpCs parent, PQualifiers child,
			Heritage parentHrtgCopy, List astName) throws AttrEvalException {
		parentHrtgCopy.setContextIsPropertyPrimary(false);
        parentHrtgCopy.setContextIsPropPostfixTail(false);
        parentHrtgCopy.setContextIsOclOpWithTypeArg(false);
        return parentHrtgCopy;
	}

	// Heritage for PropertyCall Expression
	public Heritage insideAPropertyPrimaryExpCs_computeHeritageFor_PropCall(
			APropertyPrimaryExpCs parent, PPropertyCallExpCs child,
			Heritage parentHrtgCopy) throws AttrEvalException {
		// GMC: posem a cert per indicar que estem en el context d'un atribut
		parentHrtgCopy.setContextIsPropertyPrimary(true);
        parentHrtgCopy.setContextIsPropPostfixTail(false);
        return parentHrtgCopy;
	}
	public Heritage insideAPropPostfixExpTailCs_computeHeritageFor_PropCall(
			APropPostfixExpTailCs parent, PPropertyCallExpCs child,
			Heritage parentHrtgCopy) throws AttrEvalException {
		parentHrtgCopy.setContextIsPropertyPrimary(false);
        parentHrtgCopy.setContextIsPropPostfixTail(true);
        return parentHrtgCopy;        
	}

	
	
	//************************************************************************
	//*************** Additional Cases of Call Expressions *******************
	//************************************************************************
    /**
     * Creates an OperationCallExp instance for an operation which takes a type
     * argument.
     * @param astOpName
     * 			Name of the operation taking the type argument
     * @param source
     * 			OclExpression representing the source expression
     * @param astParameters
     * 			List containing a single instance of OclActualParameterListItem
     * @return
     * 			OperationCallExp representing the specified operation.
     * @throws AttrEvalException
     */
    protected OperationCallExp createOpWithTypeArg(String astOpName, OclExpression source, List astParameters) throws AttrEvalException {
	    if (astParameters == null) throw new AttrEvalException("parameter list must not be null for " +
	    		"ocl operations with type argument");
	    if (astParameters.size() != 1) throw new AttrEvalException("exactly one parameter required for " +
	    	"ocl operations with type argument");
	    
	    Type sourceType = source.getType();
	    
	    while (sourceType instanceof CollectionType) {
	    	sourceType = ((CollectionType)sourceType).getElementType();
	    }
	    	    
	    OclActualParameterListItem param = (OclActualParameterListItem) astParameters.get(0);
	    if (! param.isTypeSpecifier()) throw new AttrEvalException("internal error: parameter must be a type specifier in this context");
	    
	    Type t = null;
	    if (astOpName.equals("oclAsType")) { 
	    	if (!(sourceType instanceof UmlClass)) throw new AttrEvalException("Source of "+astOpName+" must be an UmlClass");
	    	Collection subclasses = ut.getSubClasses((UmlClass) sourceType);
	    	t = param.getTypeSpecifierValue();
	    	if (!(t instanceof UmlClass)) throw new AttrEvalException("Type parameter of "+astOpName+" must be an UmlClass");
	    	if (!subclasses.contains(t)) throw new AttrEvalException("Semantic Error: "+t.getName()+" is not a direct subclass of "+sourceType.getName());
	    }
	    else if (astOpName.equals("oclIsTypeOf")||astOpName.equals("oclIsKindOf")) {
	    	t = uf.findDataTypeByName("Boolean");
	    }

	    if (astOpName.equals("oclIsTypeOf") && source instanceof VariableExp && source.getType() instanceof AnyType) {
	    	VariableExp vsource = (VariableExp) source;
	    	vsource.setType(param.getTypeSpecifierValue());
	    	vsource.getReferredVariable().setType(param.getTypeSpecifierValue());
	    }
	    
	    OperationCallExp oce = (OperationCallExp) factory.createNode("OperationCallExp");
	    Operation op = ocl.findOclOperationByName(astOpName);
	    oce.setName(astOpName);
	    oce.setSource(source);
	    oce.setReferredOperation(op);
	    
	    if (astOpName.equals("oclAsType")) {
	    	if (source.getType() instanceof CollectionType) {
	    		t = ut.newCollectionType(((CollectionType)source.getType()), param.getTypeSpecifierValue());
	    	} else if (source instanceof PropertyCallExp) {
	    		if (ut.hasCollectionType((PropertyCallExp)source)) {
	    			int result = ut.computeResultTypeOfNavigation((PropertyCallExp)source);
	    			if (result != ut.SET_INDIVIDUAL) {
	    				t = ut.newCollectionType((PropertyCallExp)source, param.getTypeSpecifierValue());
	    			}
	    		}
	    	}
	    }
	
	    oce.setType(t);
	    
	    TypeExp te = (TypeExp) factory.createNode("TypeExp");
	    te.setName(param.getSimpleNameValue());
	    te.setReferredType(param.getTypeSpecifierValue());
        
	    // Link between OperationCallExp and type argument
	    apcf.getAArgumentParentCall().add(te, oce);
	    return oce;
    }
	public List computeAstFor_APropertyCallParametersCs(Heritage nodeHrtg,
			List astParamList) throws AttrEvalException {
		// ensure that myAst is not null, if it is, return an empty list
        if ( astParamList == null ) {
            return (List) factory.createNode("List");
        } else {
            return astParamList;
        }
	}
	public OclExpression computeAstFor_APropPostfixExpTailCs(Heritage nodeHrtg,
			OclExpression astPropCall) throws AttrEvalException {
		// astPropCall is the correctly initialized ast node, created by        
        // lower-level production rules (namely the alternatives of property_   
        // call_exp_cs.                                                         
        return astPropCall;
	}
	private boolean isOclOpWithTypeArg(List opName) {
        if ( opName.size() != 1 ) return false;
        String name = (String) opName.get(0);
        boolean result = name.equals("oclIsTypeOf") ||
                name.equals("oclIsKindOf") ||
                name.equals("oclAsType");
        return result;
	}
	
	public OclExpression computeTimeExp(OclExpression original, boolean ispost, OclTimeExp astTime) throws AttrEvalException {
		if ( astTime == null ) { 
            return original;
        }       
		OclExpression result = null;
		if (ispost) {        
			if ( astTime == OclTimeExp.AT_PRE ) {
				result = oclExpressionWithAtPre(original, "@pre");
			} else {
				throw new AttrEvalException("Unsupported time expression" + astTime);
			}
		}
        return result;
	}
	private OperationCallExp oclExpressionWithAtPre(OclExpression exp, String opName) throws AttrEvalException {
        OperationCallExp result = (OperationCallExp) factory.createNode("OperationCallExp");
        result.setName(opName);
        result.setSource(exp);
        Type exptype = null;
        exptype = ut.getCorrectType(exp);
        result.setType(exptype);
        Operation refop = this.ocl.findOclOperationByName("@pre");
        assert ( refop != null ):
            "Referred operation " + opName + " does not exist in type " + exptype.getName();
        result.setReferredOperation(refop);
        return result;
    }

	
	// Heritage methods
	public Heritage insideAArgListPropertyCallExpCs_computeHeritageFor_Parameters(
			AArgListPropertyCallExpCs parent, PPropertyCallParametersCs child,
			Heritage parentHrtgCopy, List astName, OclTimeExp astTime)
			throws AttrEvalException {
		// inside the parameter list, we start with a "fresh context" and don't 
        // consider the expressions to be inside a postfix or primary expression
        // on the first hand    
        parentHrtgCopy.setContextIsPropPostfixTail(false);
        parentHrtgCopy.setContextIsPropertyPrimary(false);
        parentHrtgCopy.setContextIsOclOpWithTypeArg(isOclOpWithTypeArg(astName));
        return parentHrtgCopy;
	}
	public Heritage insideAPropertyCallParametersCs_computeHeritageFor_ParamList(
			APropertyCallParametersCs parent, PActualParameterListCs child,
			Heritage parentHrtgCopy) throws AttrEvalException {
		parentHrtgCopy.setContextIsIteratorVarDecl(false);
        return parentHrtgCopy;
	}
	
	
	
	//************************************************************************
	//*************************** Qualifiers *********************************
	//************************************************************************
	public List computeAstFor_AQualifiersListCs(Heritage nodeHrtg,
			String astElement, List astTail) throws AttrEvalException {
		List result = null;
        // qualifier is syntactically optional (check for presence)
        if ( astTail != null ) {
            // qualifier exists
            astTail.add(astElement);
            result = astTail;
        } else {
            result = (List) factory.createNode("List");
            result.add(astElement);
        }
        return result;
	}

	// Heritage for Qualifiers
	public Heritage insideAQualifiers_computeHeritageFor_QualifiersListCs(
			AQualifiers parent, PQualifiersListCs child, Heritage parentHrtgCopy)
			throws AttrEvalException {
		return parentHrtgCopy;
	}

	
	//************************************************************************
	//********************** Logical Expressions *****************************
	//************************************************************************
	public OperationCallExp computeAstFor_ABinaryLogicalExpCs(
			OperationCallExp myAst, Heritage nodeHrtg,
			OclExpression astOperand, List astListTail)
			throws AttrEvalException {
			return computeAstForAGenericBinaryExpCs(myAst, nodeHrtg, astOperand, astListTail);
	}
	public OclBinaryExpTail computeAstFor_ALogicalExpTailCs(
			OclBinaryExpTail myAst, Heritage nodeHrtg, String astOperator,
			OclExpression astOperand) throws AttrEvalException {
		myAst.setOperand(astOperand);
		myAst.setOperator(astOperator); // and, or, xor, implies
		return myAst;
	}
		
	
	//************************************************************************
	//********************** Relational Expressions **************************
	//************************************************************************
	public OperationCallExp computeAstFor_ABinaryRelationalExpCs(
			OperationCallExp myAst, Heritage nodeHrtg,
			OclExpression astOperand, OclBinaryExpTail astTail)
			throws AttrEvalException {
		if (verbose) System.out.println("Begin of OperationCallExp");
		//Set astOperand (left expression) as source into OperationCallExp
		myAst.setSource(astOperand);
			
		// Obtain the operator and the right expression
		String operator = astTail.getOperator();
		OclExpression rightOperand = astTail.getOperand();
		
		// Find the Operation
		Operation op = this.ocl.findOclOperationByName(operator);
	    // the operation must exist
		if ( op == null ) {
		    throw new AttrEvalException("Operation " + operator + " not exists in the model");
		}
		if (verbose) System.out.println("Operation '"+operator+ "' OK");
		myAst.setReferredOperation(op);
    	
		// Link between OperationCallExp and right Expression
    	apcf.getAArgumentParentCall().add(rightOperand, myAst);
    	
    	// check the correctness of the OperationCallExp
    	tc.typecheck(myAst);
    	
    	if (verbose) System.out.println("OperationCallExp has been created successfully");
    	return myAst;
	}	
	public OclBinaryExpTail computeAstFor_ARelationalExpTailCs(
			OclBinaryExpTail myAst, Heritage nodeHrtg, String astOperator,
			OclExpression astOperand) throws AttrEvalException {
		// OclBinaryExpTail is an auxiliary class that allows to flow the 
		// information from children to parent
		// right operand 
		myAst.setOperand(astOperand);
		// Operator : String that can be one of the follow 
		//	<, >, <=, >=, = or <>
        myAst.setOperator(astOperator);
        return myAst;
	}
	
		
	//************************************************************************
	//********************** Additive Expressions ****************************
	//************************************************************************
	public OperationCallExp computeAstFor_ABinaryAdditiveExpCs(
			OperationCallExp myAst, Heritage nodeHrtg,
			OclExpression astOperand, List astListTail)
			throws AttrEvalException {
		return computeAstForAGenericBinaryExpCs(myAst, nodeHrtg, astOperand, astListTail);
	}
	public OclBinaryExpTail computeAstFor_AAdditiveExpTailCs(
			OclBinaryExpTail myAst, Heritage nodeHrtg, String astOperator,
			OclExpression astOperand) throws AttrEvalException {
		myAst.setOperand(astOperand);
		myAst.setOperator(astOperator); // +, -, ...
		return myAst;
	}
		
	
	
	//************************************************************************
	//******************** Multiplicative Expressions ************************
	//************************************************************************
	public OperationCallExp computeAstFor_ABinaryMultiplicativeExpCs(
			OperationCallExp myAst, Heritage nodeHrtg,
			OclExpression astOperand, List astListTail)
			throws AttrEvalException {
		return computeAstForAGenericBinaryExpCs(myAst, nodeHrtg, astOperand, astListTail);
	}
	public OclBinaryExpTail computeAstFor_AMultiplicativeExpTailCs(
			OclBinaryExpTail myAst, Heritage nodeHrtg, String astOperator,
			OclExpression astOperand) throws AttrEvalException {
		myAst.setOperand(astOperand);
		myAst.setOperator(astOperator); // *, /
		return myAst;
	}
	
	
	
	//************************************************************************
	//*********************** Unary Expressions ******************************
	//************************************************************************
	public OperationCallExp computeAstFor_AUnaryOpUnaryExpCs(
			OperationCallExp myAst, Heritage nodeHrtg, String astOperator,
			OclExpression astOperand) throws AttrEvalException {
		
		// Put astOperand (the right expression) as source of the OperationCallExp 
		myAst.setSource(astOperand);
		
		Operation op = ocl.findOclOperationByName(astOperator);
		// the operator must exist
        if ( op == null ) {
        	throw new AttrEvalException("The operator " + astOperator + " does not exist in the model");
    	}
        if (verbose) System.out.println("Operation ''"+astOperator+ "'' OK");
        // Put the Operation linked with the OperationCallExp
        myAst.setReferredOperation(op);
		
        tc.typecheck(myAst);
        
        return myAst;
	}

	
	
	//************************************************************************
	//***************** Primary and WithTail Expressions *********************
	//************************************************************************
	public OclExpression computeAstFor_APropertyPrimaryExpCs(Heritage nodeHrtg,
			OclExpression astPropCall) throws AttrEvalException {
		// #customheritage does not support #chain yet (hence this method)
        return astPropCall;
	}
	public OclExpression computeAstFor_AWithTailPostfixExpCs(Heritage nodeHrtg,
			OclExpression astLeftmostExp, OclExpression astTreePostfixExpTailCs)
			throws AttrEvalException {
        // source expression and list of postfix tails is already converted to  
        // a tree, so we just need to return it.                                
        return astTreePostfixExpTailCs;
	}
	
	// Heritage
	public Heritage insideAWithTailPostfixExpCs_computeHeritageFor_PostfixExpTailCs(
			AWithTailPostfixExpCs parent, PPostfixExpTailCs child,
			Heritage parentHrtgCopy, OclExpression astPreviousSibling,
			OclExpression astLeftmostExp) throws AttrEvalException {
		if ( astPreviousSibling != null ) {
            // we have a previous sibling. set it as source expression  
            parentHrtgCopy.setCurrentSourceExpression(astPreviousSibling);
            parentHrtgCopy.setCurrentLeftSibling(astPreviousSibling);
        } else if ( astLeftmostExp != null ) {
            parentHrtgCopy.setCurrentSourceExpression(astLeftmostExp);            
            parentHrtgCopy.setCurrentLeftSibling(astLeftmostExp);
        } else {
            throw new AttrEvalException("Internal error: both leftmost source " +
                " expression and left sibling postfix expression is null (need" +
                " source expression)");
        }
        return parentHrtgCopy;  
	}

	
	//************************************************************************
	//********************* Literal Expressions ******************************
	//************************************************************************
	public IntegerLiteralExp computeAstFor_AIntNumericLiteralExpCs (
			IntegerLiteralExp myAst, Heritage nodeHrtg, Integer astInteger)
			throws AttrEvalException {
		
		myAst.setIntegerSymbol(astInteger.intValue());
		myAst.setName("");
		tc.typecheck(myAst);
	
		if (verbose) System.out.println("IntegerLiteralExp created for the integer "+astInteger.intValue());
        return myAst;
	}
	public RealLiteralExp computeAstFor_ARealNumericLiteralExpCs(
			RealLiteralExp myAst, Heritage nodeHrtg, Double astReal)
			throws AttrEvalException {
		
		myAst.setRealSymbol(astReal.floatValue());
		myAst.setName("");
		
		tc.typecheck(myAst);
		
		if (verbose) System.out.println("RealLiteralExp created for the float "+astReal.floatValue());
        return myAst;
	}		
	public BooleanLiteralExp computeAstFor_AFalseBooleanLiteralExpCs(
			Heritage nodeHrtg, Boolean astFalse) throws AttrEvalException {
		
		BooleanLiteralExp ble = (BooleanLiteralExp) factory.createNode("BooleanLiteralExp");
		ble.setBooleanSymbol(astFalse.booleanValue());
		ble.setName("");
		
		tc.typecheck(ble);
		
		if (verbose) System.out.println("BooleanLiteralExp created for the boolean "+astFalse.booleanValue());
		return ble;
	}
	public BooleanLiteralExp computeAstFor_ATrueBooleanLiteralExpCs(
			Heritage nodeHrtg, Boolean astTrue) throws AttrEvalException {
		
		BooleanLiteralExp ble = (BooleanLiteralExp) factory.createNode("BooleanLiteralExp");
		ble.setBooleanSymbol(astTrue.booleanValue());
		ble.setName(astTrue.toString());
		
		tc.typecheck(ble);
		
		if (verbose) System.out.println("BooleanLiteralExp created for the boolean "+astTrue.booleanValue());
		return ble;
	}
	public StringLiteralExp computeAstFor_AStringLiteralExpCs(
			StringLiteralExp myAst, Heritage nodeHrtg, String astValue)
			throws AttrEvalException {
		astValue = astValue.substring(1, astValue.length()-1);
		myAst.setStringSymbol(astValue);
		myAst.setName(astValue);
		
		tc.typecheck(myAst);
		
		if (verbose) System.out.println("StringLiteralExp created for the String "+astValue);
		return myAst;
	}
	
	
	
	
	//************************************************************************
	//*********************** Literals from Tokens ***************************
	//************************************************************************
	public Boolean createNodeFor_TTrue(TTrue node, Heritage nodeHrtg)
		throws AttrEvalException {	
		return Boolean.TRUE;
	}
	public Boolean createNodeFor_TFalse(TFalse node, Heritage nodeHrtg)
			throws AttrEvalException {
		return Boolean.FALSE;
	}
	public Integer createNodeFor_TIntegerLiteral(TIntegerLiteral node,
			Heritage nodeHrtg) throws AttrEvalException {
		
		Integer result = null;
        try {
            result = new Integer(node.getText());
        } catch (NumberFormatException nfe) {
            throw new AttrEvalException("Invalid numerical format for integer", nfe);
        }
        return result;
	}	
	public Double createNodeFor_TRealLiteral(TRealLiteral node,
			Heritage nodeHrtg) throws AttrEvalException {
		
		String text = node.getText();
        double realLiteralValue = 0.0;
        try { 
            realLiteralValue = java.lang.Double.valueOf(text).doubleValue();
        } catch (NumberFormatException nfe) {
            throw new AttrEvalException("Invalid numeric format of real literal '" + text + "'", nfe);
        }
        Double result = new Double(realLiteralValue);
		return result;
	}
	
	
	
	//************************************************************************
	//***************************** IteratorExp ******************************
	//************************************************************************
	public IteratorExp computeAstFor_AIteratorArrowPropertyCallExpCs(
			IteratorExp myAst, Heritage nodeHrtg, String astName,
			List astIterators, OclExpression astBody) throws AttrEvalException {
        Heritage hrtg = nodeHrtg;
        //Environment env = hrtg.getEnv();

        // Obtain source from Heritage
        OclExpression source = hrtg.getCurrentSourceExpression();
        if ( source == null )                  // already checked in computeEnvFor_Body
            throw new AttrEvalException("Source expression must not be null");   
        
        Type sourceType = source.getType();
        if (source instanceof PropertyCallExp) {
        	PropertyCallExp pce = (PropertyCallExp) source;
        	int resulttype = ut.computeResultTypeOfNavigation(pce);
        	sourceType = ut.createTypeForCollectionPCE(resulttype, pce.getType());
        }
        
        if ( sourceType == null )              // already checked in computeEnvFor_Body
        	throw new AttrEvalException("Type of source expression must not be null");
            
        boolean sourceIsCollection = sourceType instanceof CollectionType;
        if ( !sourceIsCollection )              // already checked in computeEnvFor_Body
        	throw new AttrEvalException("Source expression must have a collection type for IteratorExp");
            
        // for "one" and "isUnique", only one iterator is allowed
        if (    ( astIterators != null) &&
                ( astName.equals("one") || astName.equals("isUnique") )
           ) {
            int iterCount = astIterators.size();
            if ( iterCount > 1 ) {
                throw new AttrEvalException("For iterators 'isUnique' and 'equals', at most one " +
                    "iterator variable declaration is allowed");
            }
        }            
            
        CollectionType collType = (CollectionType) sourceType;
        Type elemType = (Type) collType.getElementType();
        
        this.tr.setDefaultType(elemType);
        List transformedIterators = null;
        if ( astIterators != null ) {
            transformedIterators = this.tr.transform(astIterators);
        }
 
        // initialize ast node's attributes
        Collection iteratorsInExp = myAst.getIterator();
        iteratorsInExp.clear();
        AIteratorLoopExpFacade ailef = new AIteratorLoopExpFacade(p);
        Variable v = (Variable) factory.createNode("Variable");
        
        if ( ( transformedIterators != null ) && ( transformedIterators.size() > 0 ) ) {
            Iterator itAstIt = transformedIterators.iterator();
            while ( itAstIt.hasNext() ) {
                v = (Variable) itAstIt.next();
                ailef.getAIteratorLoopExp().add(v,myAst);
            }
        } else {
            // if no iterator variable is explicitly given, we have to create one  
            v.setName(ANONYMOUS_ITERATOR_NAME); 
            v.setType(elemType);
            // ADD default ITERATOR into LOOPEXP
            ailef.getAIteratorLoopExp().add(v,myAst);
        }
        
        // WARNING
        // body type must be boolean for some iterator types, but this is       
        // sorted out by the type checker (I believe).
        
        myAst.setBody(astBody);
        myAst.setSource(source);
        myAst.setName(astName);
        
        tc.typecheck(myAst);
        
        // Check if iterator variables are used inside the iterator body
        ut.allVariablesUsed(myAst);
        
        return myAst;
	}
	
	//Heritage for Iterator Expression
	public Heritage insideAIteratorArrowPropertyCallExpCs_computeHeritageFor_Body(
			AIteratorArrowPropertyCallExpCs parent, PExpression child,
			Heritage parentHrtgCopy, String astName, List astIterators)
			throws AttrEvalException {
        Environment oldEnv = parentHrtgCopy.getEnv();
        WritableEnvironment newEnv = oldEnv.nestedEnvironment();

        parentHrtgCopy.setContextIsIteratorExp(true);

        OclExpression source = parentHrtgCopy.getCurrentSourceExpression();
        Type sourceType = tc.typecheck(source);
        if (source instanceof PropertyCallExp) {
        	PropertyCallExp pce = (PropertyCallExp) source;
        	int resulttype = ut.computeResultTypeOfNavigation(pce);
        	if (ut.hasCollectionType(pce)) {
        		sourceType = ut.createTypeForCollectionPCE(resulttype,pce.getType());
        	}
        }
        
        if (verbose) System.out.println("source for "+astName+" is "+sourceType.getName());
        if ( sourceType == null )
            throw new AttrEvalException("Type of source expression for "+astName+" must not be null");
        
        boolean sourceIsCollection = sourceType instanceof CollectionType;
        
        if ( ! sourceIsCollection ) {
            throw new AttrEvalException("Source expression must have a collection type for IteratorExp "+astName);
        }

        CollectionType collType = (CollectionType) sourceType;
        Type elemType = collType.getElementType();
        if ( elemType == null )
        	throw new AttrEvalException("Element type of source collection for "+astName+" is null");
            
        this.tr.setDefaultType((Classifier) elemType);
        List transformedIterators = null;
        // iterators available?
        if ( astIterators != null ) {
            transformedIterators = this.tr.transform(astIterators);
        }
        if ( astIterators == null || astIterators.size() == 0 ) {
            // create one implicit iterator variable
            transformedIterators = new ArrayList(1);
            Variable v = (Variable) factory.createNode("Variable");
            v.setType(elemType);
            v.setName(ANONYMOUS_ITERATOR_NAME);
            transformedIterators.add(v);
            parentHrtgCopy.setLacksExplicitIteratorVariables(true);
            parentHrtgCopy.setImplicitIteratorVariable(v);
        }
        
        Iterator it = transformedIterators.iterator();
        try {
            while ( it.hasNext() ) {
                Object o = it.next();
                // System.out.println("object type: " + o.getClass().getName());
                Variable v = (Variable) o;
                newEnv.addElement(v.getName(), v, true);
            }
        } catch (DuplicateNameException dne) {
            rethrowDNE(dne, "creation of environment for body of IteratorExp");
        }
        parentHrtgCopy.setEnv(newEnv);
        return parentHrtgCopy;
	}
	public Heritage insideAIteratorArrowPropertyCallExpCs_computeHeritageFor_Iterators(
			AIteratorArrowPropertyCallExpCs parent, PIteratorVarsCs child,
			Heritage parentHrtgCopy, String astName) throws AttrEvalException {
		parentHrtgCopy.setContextIsIteratorVarDecl(true);
        parentHrtgCopy.setContextIsIteratorExp(true);
        return parentHrtgCopy;
	}
	public Heritage insideAIteratorVarsCs_computeHeritageFor_Iterators(
			AIteratorVarsCs parent, PActualParameterListCs child,
			Heritage parentHrtgCopy) throws AttrEvalException {
		parentHrtgCopy.setContextIsIteratorVarDecl(true);
        return parentHrtgCopy;
	}
	

	
	//************************************************************************
	//***************************** IterateExp ******************************
	//************************************************************************
	public IterateExp computeAstFor_AIterateArrowPropertyCallExpCs(
			IterateExp myAst, Heritage nodeHrtg, String astIterate,
			List astIterators, Variable astAccumulator, OclExpression astBody)
			throws AttrEvalException {
		 Heritage hrtg = nodeHrtg;
		 Environment env = hrtg.getEnv();

		 OclExpression source = hrtg.getCurrentSourceExpression();

		 if(source == null) {
			 throw new AttrEvalException("Error: source expression must not be null.");
	     }

	     // obtain type of source
	     Type sourceType = tc.typecheck(source);
	     if (source instanceof PropertyCallExp) {
	      	PropertyCallExp pce = (PropertyCallExp) source;
	       	int resulttype = ut.computeResultTypeOfNavigation(pce);
	       	if (ut.hasCollectionType(pce)) {
	       		sourceType = ut.createTypeForCollectionPCE(resulttype,pce.getType());
	       	}
	     }

	     if(sourceType == null) {
	       	throw new AttrEvalException("Type of source expression must not be null.");
	     }

	     boolean sourceIsCollection = sourceType instanceof CollectionType;
	     if(!sourceIsCollection) {
	       	throw new AttrEvalException("source expression must have a collection type for IterateExp");
	     }
	     
	     CollectionType collType = (CollectionType) sourceType;
	     Type elemType = collType.getElementType();

	     this.tr.setDefaultType(elemType);
	     List transformedIterators = this.tr.transform(astIterators);
	     
	     myAst.setName(astIterate);
	     myAst.setResult(astAccumulator);
	     myAst.setSource(source);
	     myAst.setBody(astBody);

	     Collection iteratorsInExp = myAst.getIterator();
	     iteratorsInExp.clear();
	     AIteratorLoopExpFacade ailef = new AIteratorLoopExpFacade(p);
	     Variable v;
	     
	     if ( ( transformedIterators != null ) && ( transformedIterators.size() > 0 ) ) {
	    	 Iterator itAstIt = transformedIterators.iterator();
	    	 while ( itAstIt.hasNext() ) {
	    		 v = (Variable) itAstIt.next();
	    		 ailef.getAIteratorLoopExp().add(v,myAst);
	    	 }
	     } else {
	    	 v = (Variable) factory.createNode("Variable");
	         v.setName(this.anonIterVars.getNextAsString());
	         ailef.getAIteratorLoopExp().add(v,myAst);
	     }

	     // assert all vardecls have a type, either explicitly declared or
	     // implicitly derived from the collection's element type
	     Iterator itIt = iteratorsInExp.iterator();
	     while ( itIt.hasNext() ) {
	    	 Variable curV = (Variable) itIt.next();
	    	 Type curType = curV.getType();   
	    	 if ( curType == null ) {
	    		 // set type from collections element type 
                curV.setType(elemType);
	    	 }
	     }

	     tc.typecheck(myAst);
	     
	     return myAst;
	}
	
	//Heritage for Iterate Expression
	public Heritage insideAIterateArrowPropertyCallExpCs_computeHeritageFor_Body(
			AIterateArrowPropertyCallExpCs parent, PExpression child,
			Heritage parentHrtgCopy, String astIterate, List astIterators,
			Variable astAccumulator) throws AttrEvalException {
		Environment oldEnv = parentHrtgCopy.getEnv();
        WritableEnvironment newEnv = oldEnv.nestedEnvironment();

        parentHrtgCopy.setContextIsIteratorExp(true);

        OclExpression source = parentHrtgCopy.getCurrentSourceExpression();
        if(source == null) {
           throw new AttrEvalException("Error: source expression must not be null.");
        }
        // obtain type of source
        Type sourceType = tc.typecheck(source);
        if (source instanceof PropertyCallExp) {
        	PropertyCallExp pce = (PropertyCallExp) source;
        	int resulttype = ut.computeResultTypeOfNavigation(pce);
        	if (ut.hasCollectionType(pce)) {
        		sourceType = ut.createTypeForCollectionPCE(resulttype,pce.getType());
        	}
        }

        if(sourceType == null) {
        	throw new AttrEvalException("Type of source expression must not be null.");
        }

        boolean sourceIsCollection = sourceType instanceof CollectionType;
        if(!sourceIsCollection) {
        	throw new AttrEvalException("source expression must have a collection type for IterateExp");
        }

        CollectionType collType = (CollectionType) sourceType;
        Type elemType = collType.getElementType();
        
        this.tr.setDefaultType(elemType);
        List transformedIterators = null;
        // iterators available?
        if ( astIterators != null ) {
            transformedIterators = this.tr.transform(astIterators);
        }

        if ( astIterators == null || astIterators.size() == 0 ) {
            transformedIterators = new ArrayList(1);
            Variable v = (Variable) factory.createNode("Variable");
            v.setType(elemType);
            v.setName(ANONYMOUS_ITERATOR_NAME);
            transformedIterators.add(v);
            parentHrtgCopy.setLacksExplicitIteratorVariables(true);
            parentHrtgCopy.setImplicitIteratorVariable(v);
        }
        Iterator it = transformedIterators.iterator();
        try {
            while ( it.hasNext() ) {
                Variable v = (Variable) it.next();
                newEnv.addElement(v.getName(), v, true);
            }
            newEnv.addElement(astAccumulator.getName(), astAccumulator, true);
        } catch (DuplicateNameException dne) {
            rethrowDNE(dne, "creation of environment for body of IterateExp");
        }
        parentHrtgCopy.setEnv(newEnv);
        return parentHrtgCopy;
	}
	public Heritage insideAIterateArrowPropertyCallExpCs_computeHeritageFor_Iterators(
			AIterateArrowPropertyCallExpCs parent, PIterateVarsCs child,
			Heritage parentHrtgCopy, String astIterate)
			throws AttrEvalException {
		parentHrtgCopy.setContextIsIteratorVarDecl(true);
        return parentHrtgCopy;
	}
	public Heritage insideAIterateVarsCs_computeHeritageFor_Iterators(
			AIterateVarsCs parent, PActualParameterListCs child,
			Heritage parentHrtgCopy) throws AttrEvalException {
		parentHrtgCopy.setContextIsIteratorVarDecl(true);
        return null;
	}

	
	
	//************************************************************************
	//************************** Arrow Operations ****************************
	//************************************************************************
	// Methods for collection operations
	public OperationCallExp computeAstFor_AOperationArrowPropertyCallExpCs(
			OperationCallExp myAst, Heritage nodeHrtg, String astName, 
			List astParameters) throws AttrEvalException {
		
		OclExpression source = nodeHrtg.getCurrentSourceExpression();

		if (!tc.isCollectionOp(astName)) throw new AttrEvalException("Semantic Error: "+astName+" is not a valid arrow operation.");
		
		Operation op = ocl.findOclOperationByName(astName);
		myAst.setSource(source);
		myAst.setReferredOperation(op);
		if (astParameters!=null) {
			AArgumentParentCallFacade aapcf = new AArgumentParentCallFacade(this.p);
			AArgumentParentCall aapc = aapcf.getAArgumentParentCall();
			for (Iterator iterator = astParameters.iterator(); iterator.hasNext();) {
				OclExpression param = (OclExpression) iterator.next();
				aapc.add(param, myAst);
			}
		}
		tc.typecheck(myAst);
		return myAst;
	}
	
	//Heritage for Arrow Operations
	public Heritage insideAOperationArrowPropertyCallExpCs_computeHeritageFor_Parameters(
			AOperationArrowPropertyCallExpCs parent,
			PActualParameterListCs child, Heritage parentHrtgCopy,
			String astName) throws AttrEvalException {
		//TODO if necessary do anything more
		return parentHrtgCopy;
	}	
	
	
	//************************************************************************
	//***************************** Parameters *******************************
	//************************************************************************
	public List computeAstFor_AActualParameterListCs(Heritage nodeHrtg,
			OclActualParameterListItem astElement, List astTail)
			throws AttrEvalException {
		// tail is syntactically optional (check for presence)
        if ( astTail == null ) {
            // tail does not exist, create list instance
            astTail = (List) factory.createNode("List");
        }
        Heritage hrtg = nodeHrtg;
        boolean isIteratorVarDecl = hrtg.isContextIsIteratorVarDecl();
        if ( isIteratorVarDecl ) {
            if(! ( astElement.isFormalParameter() || astElement.isSimpleName() ))
                throw new AttrEvalException("inside an iterator variable declaration, a list of simple names and formal parameter declaration is required");
            
            Variable vd = (Variable) factory.createNode("Variable");
            if ( astElement.isSimpleName() ) {
                vd.setName(astElement.getSimpleNameValue());
            } else if ( astElement.isFormalParameter() ) {
                OclFormalParameter fp = astElement.getFormalParameterValue();
                vd.setName(fp.getName());
                Type type = fp.getType();
                if ( type == null )
                	throw new AttrEvalException("'type' of a formal parameter must be defined, but is not for iterator variable '" + fp.getName() + "'");
                vd.setType(type);
            }
            astTail.add(0, astElement);
        } else {
            if ( hrtg.isContextIsOclOpWithTypeArg() ) {
                if ( !astElement.isTypeSpecifier() )
                	throw new AttrEvalException("only type specifiers allowed in production actual_parameter_list_cs in a type argument list");
                astTail.add(0, astElement);
            } else {
                if( !astElement.isFullExpression() )
                	throw new AttrEvalException("only full OCL expressions allowed in production actual_parameter_list_cs outside an iterator variable declaration");
                OclExpression exp = astElement.getFullExpressionValue();
                astTail.add(0, exp);
            }
        }
        return astTail;
	}
	public OclFormalParameter computeAstFor_AFormalParameterCs(
			OclFormalParameter myAst, Heritage nodeHrtg, String astName,
			Classifier astType) throws AttrEvalException {
		if ( astName == null ) throw new AttrEvalException("Name of formal parameter must not be null");
        if ( astType == null ) throw new AttrEvalException("Type of formal parameter must not be null");
        myAst.setName(astName);
        myAst.setType(astType);
        
        return myAst;
	}
	public OclActualParameterListItem computeAstFor_ATypedActualParameterListElementCs(
			OclActualParameterListItem myAst, Heritage nodeHrtg,
			OclFormalParameter astParam) throws AttrEvalException {
		if (astParam.getName() == null)
            throw new AttrEvalException("name of formal parameter must be set");
        // this cannot be null, since a simple name is recognized as path name
        // and as such will be covered by AUntypedActualParameterListElementCs
        if (astParam.getType() == null)
        	throw new AttrEvalException("type of formal parameter must be set");       
        myAst.setFormalParameterValue(astParam);
        return myAst;
	}
	public OclActualParameterListItem computeAstFor_AUntypedActualParameterListElementCs(
			OclActualParameterListItem myAst, Heritage nodeHrtg,
			OclExpression astElement) throws AttrEvalException {
		
		Heritage hrtg = nodeHrtg;
        boolean contextIsIteratorVarDecl = hrtg.isContextIsIteratorVarDecl();
        boolean contextIsOclOpWithTypeArg = hrtg.isContextIsOclOpWithTypeArg();
        
        if ( contextIsIteratorVarDecl ) {
            // allowed: simple name/identifier 
            if (!(astElement instanceof VariableExp)) 
                throw new AttrEvalException("inside an iterator variable declaration, path_time_property_exp " +
                "must return an instance of VariableExp, but found java class " + astElement.getClass().getName());
            
            myAst.setSimpleNameValue(astElement.getName());
            return myAst;

        // When OCL Operations be added to the system, check this case below
        } else if ( contextIsOclOpWithTypeArg ) {
            // allowed: path names identifying a classifier or a DataType
            if (! (astElement instanceof VariableExp || astElement instanceof TypeExp)) 
            	throw new AttrEvalException("inside an ocl operation with type argument, path_time_property_exp " +
                "must return an instance of VariableExp or TypeExp, but found java class "+astElement.getClass().getName());
            Type t = (Type) astElement.getType();
            if ( t == null ) throw new AttrEvalException("internal error: type of type argument not found");
            myAst.setTypeSpecifierValue(t);
            return myAst;
        } else {
            // allowed/expected: full ocl expression
            myAst.setFullExpressionValue(astElement);
            return myAst;
        }
	}
	public List computeAstFor_AFormalParameterListCs(Heritage nodeHrtg,
			OclFormalParameter astItem, List astTail) throws AttrEvalException {
		List result = null;
        if ( astTail == null ) {
            result = new LinkedList();
            result.add(astItem);
        } else {
            result = astTail;
            result.add(0,  astItem);
        }
        return result;
	}
	
	
	
	//************************************************************************ 
	//***************************** Variables ********************************
	//************************************************************************
	public Variable computeAstFor_AInitializedVariableCs(Variable myAst,
			Heritage nodeHrtg, OclFormalParameter astNameAndType,
			OclExpression astInitializer) throws AttrEvalException {
		OclFormalParameter nat = astNameAndType;
        myAst.setName(nat.getName());
        myAst.setType(nat.getType());
        myAst.setInitExpression(astInitializer);
        
        tc.evaluateInitializedVariable(myAst);
        return myAst;
	}
	public List computeAstFor_AInitializedVariableListCs(Heritage nodeHrtg,
			Variable astItem, List astListTail) throws AttrEvalException {
		 List result = null;
        if ( astListTail != null ) {
            astListTail.add(0, astItem);
            result = astListTail;
        } else {
            result = new LinkedList();
            result.add(astItem);
        }
        return result;
	}

	
	
	//************************************************************************ 
	//************************** Operation Signature *************************
	//************************************************************************
	public OclOperationSignature computeAstFor_AOperationSignatureCs(
			OclOperationSignature myAst, Heritage nodeHrtg, List astParameters,
			Classifier astReturnType) throws AttrEvalException {
		// formal parameter list is optional (may be null)  
        if ( astParameters == null ) {
            // install empty list as formal parameter list  
            myAst.getFormalParameters().clear();
        } else {
            myAst.setFormalParameters(astParameters);
        }
        // return type is optional (may be null)            
        myAst.setReturnType(astReturnType);
        return myAst;
	}
	
	
	
	//************************************************************************
	//***************************** Type Specifier ***************************
	//************************************************************************
	public Classifier computeAstFor_ASimpleTypeSpecifierCs(Heritage nodeHrtg,
			List astPathNameCs) throws AttrEvalException {
		if (astPathNameCs.size()==1) {
			String className = (String) astPathNameCs.get(0);
			Classifier cls = uf.findClassByName(className);
			if (cls != null) {
				return cls;
			} else {
				cls = uf.findDataTypeByName(className);
				if (cls == null) {
					throw new AttrEvalException("Invalid or inexistent name for iterator variable type. By now, only can use DataType names or Class names.");
				}
				return cls;
			}
		}
		throw new AttrEvalException("Invalid or inexistent name for iterator variable type");
	}
	
	
	
	//************************************************************************
	//************** Collection and Tuple Type Specifier *********************
	//************************************************************************
	public CollectionKind computeAstFor_AOrderedSetCollectionTypeIdentifierCs(
			Heritage nodeHrtg) throws AttrEvalException {
		return CollectionKindEnum.ORDERED_SET;
	}
	public CollectionKind computeAstFor_ASequenceCollectionTypeIdentifierCs(
			Heritage nodeHrtg) throws AttrEvalException {
		return CollectionKindEnum.SEQUENCE;
	}
	public CollectionKind computeAstFor_ASetCollectionTypeIdentifierCs(
			Heritage nodeHrtg) throws AttrEvalException {
		return CollectionKindEnum.SET;
	}
	public CollectionKind computeAstFor_ABagCollectionTypeIdentifierCs(
			Heritage nodeHrtg) throws AttrEvalException {
		return CollectionKindEnum.BAG;
	}
	public CollectionKind computeAstFor_ACollectionCollectionTypeIdentifierCs(
			Heritage nodeHrtg) throws AttrEvalException {
		// It is impossible to instanciate a CollectionType. By default we assume Collection as Bag.
		throw new AttrEvalException("Collection literal or type with collection kind 'Collection' not allowed. 'Collection' is an abstract type on OCL Metamodel");
	}
	
	public CollectionType computeAstFor_ACollectionTypeSpecifierCs(
			Heritage nodeHrtg, CollectionKind astKind, Classifier astType)
			throws AttrEvalException {
		CollectionType myAst = null;
		if ( astKind.equals( CollectionKindEnum.SET ) ) {
            myAst = (CollectionType) ut.newCollectionType("Set", astType);
        } else if ( astKind.equals( CollectionKindEnum.BAG ) ) {
        	myAst = (CollectionType) ut.newCollectionType("Bag", astType);
        } else if ( astKind.equals( CollectionKindEnum.SEQUENCE ) ) {
        	myAst = (CollectionType) ut.newCollectionType("Sequence", astType);
        } else if ( astKind.equals( CollectionKindEnum.ORDERED_SET ) ) {
        	myAst = (CollectionType) ut.newCollectionType("OrderedSet", astType);
        }else {
            throw new AttrEvalException("Unimplemented collection type " + 
            astKind.toString() );
        }                
        return myAst;  
	}
	public TupleType computeAstFor_ATupleTypeSpecifierCs(Heritage nodeHrtg,
			List astTupleMembers) throws AttrEvalException {
		TupleType tt = (TupleType) factory.createNode("TupleType");
		Property actualproperty;
		String tuplename = "TupleType(";
		
		LinkedList namesUsed = new LinkedList();
		
		for (Iterator member = astTupleMembers.iterator(); member.hasNext();) {
			OclFormalParameter actualmember = (OclFormalParameter) member.next();
			actualproperty = (Property) factory.createNode("Property");
			
			if (namesUsed.contains(actualmember.getName())) {
				throw new AttrEvalException("Tuple with more than one attribute " +
						"with the same name: "+actualmember.getName());
			}
			else namesUsed.add(actualmember.getName());
			
			actualproperty.setName(actualmember.getName());
			actualproperty.setType(actualmember.getType());
			tuplename += actualmember.getName()+":"+actualmember.getType().getName()+", ";
			try {
				actualproperty.setUmlclass(tt);
			} catch (ConstraintException e) {
				throw new AttrEvalException(e.getMessage());
			}
		}
		tuplename = tuplename.substring(0, tuplename.length()-2);
		tuplename += ")";
		tt.setName(tuplename);
		return tt;
	}

	
	
	//************************************************************************
	//******************* Collection and Tuple Literals **********************
	//************************************************************************
	public CollectionLiteralExp computeAstFor_ACollectionLiteralExpCs(
			CollectionLiteralExp myAst, Heritage nodeHrtg,
			CollectionKind astKind, List astParts) throws AttrEvalException {
        myAst.setKind(astKind);
        // AST node for "parts" can be null, since collection literals may be   
        // empty according to grammar. addAll(null) is not allowed and will     
        // throw a NullPointerException, so we check this before.               
        if ( astParts != null ) {
            for (Iterator iterator = astParts.iterator(); iterator.hasNext();) {
				CollectionLiteralPart clp = (CollectionLiteralPart) iterator.next();
				clp.setCollectionLiteralExp(myAst);
			}
        } else {            
            myAst.getPart().clear();   // make sure parts collection is empty
        }
        tc.typecheck(myAst);
        return myAst;
	}
	public List computeAstFor_ACollectionLiteralPartsCs(Heritage nodeHrtg,
			CollectionLiteralPart astPart, List astTail)
			throws AttrEvalException {
		 List result = null;
	     // tail is syntactically optional (check for presence)
	     if ( astTail != null ) {
	    	 astTail.add(0, astPart);
	    	 result = astTail;
	     } else {
	    	 result = (List) factory.createNode("List");
	    	 result.add(astPart);
	     }
	     return result;
	}
	public CollectionRange computeAstFor_ACollectionRangeCs(
			CollectionRange myAst, Heritage nodeHrtg, OclExpression astFirst,
			OclExpression astLast) throws AttrEvalException {
		myAst.setFirst(astFirst);
        myAst.setLast(astLast);
        tc.typecheck(myAst);
        return myAst;
	}
	public CollectionItem computeAstFor_ASingleExpCollectionLiteralPartCs(
			CollectionItem myAst, Heritage nodeHrtg, OclExpression astExpression)
			throws AttrEvalException {
		myAst.setItem(astExpression);
        return myAst;
	}
	public TupleLiteralExp computeAstFor_ATupleLiteralExpCs(
			TupleLiteralExp myAst, Heritage nodeHrtg, List astTuplePart)
			throws AttrEvalException {
		if (astTuplePart == null) {
            throw new AttrEvalException("Tuple parts must not be null");
		}
		TupleLiteralPart tlp = null;
		Property p = null;
		
		for (Iterator iterator = astTuplePart.iterator(); iterator.hasNext();) {
			System.out.println("Entramos");
			Variable v = (Variable) iterator.next();
			System.out.println(v.getName()+" "+v.getType().getName());
			tc.evaluateInitializedVariable(v);
			tlp = (TupleLiteralPart) factory.createNode("TupleLiteralPart");
			tlp.setName(v.getName());
			tlp.setType(v.getType());
			tlp.setValue(v.getInitExpression());
			p = ut.createPropertyFromInitVariable(v);
			tlp.setAttribute(p);
			tlp.setTupleLiteralExp(myAst);
			// finally, we have to delete the variable v
			v.refDelete();
		}
        tc.typecheck(myAst);
        return myAst;
	}

	
	//************************************************************************
	//************************ Enumeration Literals **************************
	//************************************************************************
	public EnumLiteralExp computeAstFor_AEnumLiteralExpCs(EnumLiteralExp myAst,
			Heritage nodeHrtg, String astPathNameHeadCs, String astSimpleName)
			throws AttrEvalException {
		String enumName = astPathNameHeadCs;
		String litName = astSimpleName;
		DataType dt = uf.findDataTypeByName(enumName);
		if (dt != null) {
			if (dt instanceof Enumeration) {
				Enumeration en = (Enumeration) dt;
				List literals = en.getOwnedLiteral();
				for (Iterator iterator = literals.iterator(); iterator.hasNext();) {
					EnumerationLiteral enumlit = (EnumerationLiteral) iterator.next();
					if (enumlit.getName().equals(litName)) {
						myAst.setReferredEnumLiteral(enumlit);
						myAst.setType(enumlit.getEnumeration());
						myAst.setName(enumName);
						return myAst;
					}
				}
				throw new AttrEvalException(litName+" is not an Enumeration Literal of "+enumName+" enumeration.");
			}
			else throw new AttrEvalException(enumName+"::"+litName+" is not an Enumeration Literal.");
		}
		else throw new AttrEvalException("Enumeration literal "+enumName+"::"+litName+" does not exist in the UML model.");
	}
	
	
	//************************************************************************
	//**************************** Other methods *****************************
	//************************************************************************
	public OperationCallExp computeAstForAGenericBinaryExpCs(
			OperationCallExp myAst, Heritage nodeHrtg,
			OclExpression astOperand, List astListTail)
			throws AttrEvalException {
		
		// leftmost operand
		OclExpression currentOperand = astOperand;
        
        // tail list won't be empty (assured by grammar), so we can simply  
        // iterate through the list of potential operands                   
        Iterator it = astListTail.iterator();        
        OclBinaryExpTail tailElement = (OclBinaryExpTail) it.next();
        
        // we now have two operands. if there are more tail elements in the     
        // list of operators, we would have to add them as rightmost operands   
        // of the logical expression 
        while ( it.hasNext() ) {
            // there are more elements. join currentOperand and operand tail    
            // and set this new OperationCallExp as currentOperand, which then  
            // becomes the new left operand
            OperationCallExp leftOperand = (OperationCallExp) factory.createNode("OperationCallExp");
            
            // Set the left expression as source
            leftOperand.setSource(currentOperand);
                
            // Obtain the operator and the right expression
            String operator = tailElement.getOperator();
            OclExpression tailOperand = tailElement.getOperand();
            
            // Find Operation
            Operation op = ocl.findOclOperationByName(operator);
         	// the operator must exist
            if ( op == null ) {
            	throw new AttrEvalException("Operation " + operator + " not exists in the model");
        	}
            
            if (verbose) System.out.println("Operation '"+operator+ "' OK");
            leftOperand.setReferredOperation(op);
        		
            // Link between OperationCallExp and right Expression
            apcf.getAArgumentParentCall().add(tailOperand, leftOperand);
            	
            // check the correctness of the OperationCallExp
            tc.typecheck(leftOperand);
            	
            if (verbose) System.out.println("OperationCallExp has been created successfully");
                
            // Prepare the variables for the next iteration
            currentOperand = leftOperand; //left expression
            tailElement = (OclBinaryExpTail) it.next();  //operator and right expression   
        }// end while
        
        // no more operator/operand tail elements, now unite the two remaining operands
        
        // Do the same work as in the previous while construction
        myAst.setSource(currentOperand);
        String operator = tailElement.getOperator();
        OclExpression tailOperand = tailElement.getOperand();           
        Operation op = ocl.findOclOperationByName(operator);
        if ( op == null ) {
        	throw new AttrEvalException("Operation " + operator + " not exists in the model");
    	}
        if (verbose) System.out.println("Operation '"+operator+ "' OK");
        myAst.setReferredOperation(op);
        apcf.getAArgumentParentCall().add(tailOperand, myAst);
        tc.typecheck(myAst);
        	
        if (verbose) System.out.println("Final OperationCallExp has been created successfully");
       		
		return myAst;
	}
	
	/**
     * Helper method which wraps a DuplicateNameException together with an additional
     * descriptive message into an AttrEvalException and rethrows the newly created 
     * AttrEvalException
     */
    private static void rethrowDNE(DuplicateNameException dne, String context) throws AttrEvalException {
        throw new AttrEvalException("Duplicate name '" + dne.getName() + "' " +
            "in environment, context is " + context, dne);
    }
    
    /**
     * Converts a List representation of a PathName into a java String. All 
     * list items must be String instances.
     * @return a concatenation of all list items concatenated with two colons
     * ('::') between each two.
     */
     private static String pathNameToString(List name) {
        if ( name == null ) {
            throw new RuntimeException("PathName reference may not be null for "+
                "conversion to String");
        }
        if ( name.size() == 0 ) {
            throw new RuntimeException("PathName must contain at least one "+
                "simple name for conversion to String");
        }
        if ( name.size() == 1 ) {
            return (String) name.get(0); 
        } else if ( name.size() >= 2 ) {
            StringBuffer sb = new StringBuffer(256);
            Iterator it = name.iterator();
            sb.append((String) it.next());
            while ( it.hasNext() ) {
                String elem = (String) it.next();
                sb.append("::");
                sb.append(elem);
            }
            return sb.toString();
        } else {
            throw new RuntimeException("Invalid length of PathName (length is " + name.size() );
        }
    }
    
    /**
     * Creates a Variable for "self". The type of this variable
     * declaration equals the contextual classifier, and the initial value
     * is undefined.
     */
    protected Variable createVariableForSelf(Classifier contextualClassifier) {
        Variable var = (Variable) factory.createNode("Variable");
        var.setName("self");
        var.setType(contextualClassifier);
        return var;
    }
    
    /**
     * Returns the Classifier of the source expression. For example, if the source expression
     * is the self VariableExp, then the contextual classifer is returned
     * @param source
     * @return
     */
    private Classifier getSourceClassifier(OclExpression source) {
    	if (source instanceof VariableExp) {
    		VariableExp ve = (VariableExp) source;
    		Variable v = ve.getReferredVariable();
    		if (v.getName().equals("self")) {
    			return (Classifier) v.getType();
    		}
    	}
    	return null;
    }

	
	public Variable computeAstFor_ATuplePartCs(Variable myAst,
			Heritage nodeHrtg, String astName, Classifier astType,
			OclExpression astInitializer) throws AttrEvalException {
		myAst.setName(astName);
		myAst.setInitExpression(astInitializer);
		tc.typecheck(astInitializer);
		Type tinitexp = ut.getCorrectType(astInitializer);
		Type tinit = astType;
		if (tinit == null) myAst.setType(tinitexp);
		else {
			if (tc.existsCompatibility(tinit, tinitexp)) {
				myAst.setType(tinit);
			}
			else throw new AttrEvalException("Incompatible types: "+tinit.getName()+" and "+tinitexp.getName());
		}
		return myAst;
	}

	
	public List computeAstFor_ATuplePartListCs(Heritage nodeHrtg,
			Variable astItem, List astListTail) throws AttrEvalException {
		List result = null;
        // list tail is syntactically optional (check for presence)
        if ( astListTail != null ) {
            // list tail exists
            astListTail.add(astItem);
            result = astListTail;
        } else {
            result = (List) factory.createNode("List");
            result.add(astItem);
        }
        return result;
	}

	




}   

	
